#install.packages("FNN")
#install.packages("ggrepel")
#install.packages("yardstick")
#install.packages("igraph")
#install.packages("caret")
#update.packages("caret")
#install.packages("reactable")
library(tidyverse)
library(sf)
library(raster)
library(knitr)
library(kableExtra)
library(tidycensus)
library(tigris)
library(FNN)
library(caret)
library(yardstick)
library(pscl)
library(plotROC)
library(ggrepel)
library(pROC)
library(grid)
library(gridExtra)
library(viridis)
library(igraph)
library(maptools)
library(reactable)
library(classInt)
plotTheme <- theme(
plot.title =element_text(size=12),
plot.subtitle = element_text(size=8),
plot.caption = element_text(size = 6),
axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
axis.text.y = element_text(size = 10),
axis.title.y = element_text(size = 10),
# Set the entire chart region to blank
panel.background=element_blank(),
plot.background=element_blank(),
#panel.border=element_rect(colour="#F0F0F0"),
# Format the grid
panel.grid.major=element_line(colour="#D0D0D0",size=.75),
axis.ticks=element_blank())
mapTheme <- theme(plot.title =element_text(size=12),
plot.subtitle = element_text(size=8),
plot.caption = element_text(size = 6),
axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_line(colour = 'transparent'),
panel.grid.minor=element_blank(),
legend.direction = "vertical",
legend.position = "right",
plot.margin = margin(1, 1, 1, 1, 'cm'),
legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))
#Colors: Viridis Plasma (option "C")
gray <- "#D3D3D3"
darkGray <- "#505251"
highlight <- "#0D0887FF"
palette2 <- c("#FA9E3BFF", "#0D0887FF")
palette3 <- c("#FDC926FF", "#D8576BFF", "#0D0887FF")
palette4 <- c("#FA9E3BFF", "#D8576BFF", "#9C179EFF", "#0D0887FF")
palette5 <- c("#FDC926FF", "#ED7953FF", "#D8576BFF", "#9C179EFF", "#0D0887FF")
palette10 <- c("#F0F921FF", "#FDC926FF", "#FA9E3BFF", "#ED7953FF", "#D8576BFF",
"#BD3786FF", "#9C179EFF", "#7301A8FF", "#47039FFF", "#0D0887FF")
#R Markdown Cheat sheet for reference - https://www.rstudio.com/wp-content/uploads/2015/02/rmarkdown-cheatsheet.pdf
#this function converts a column in to quintiles. It is used for mapping.
quintileBreaks <- function(df,variable) {
as.character(quantile(df[[variable]],
c(.01,.2,.4,.6,.8),na.rm=T))
}
#This function can be used to convert a polygon sf to centroids xy coords.
xyC <- function(aPolygonSF) {
as.data.frame(
cbind(x=st_coordinates(st_centroid(aPolygonSF))[,1],
y=st_coordinates(st_centroid(aPolygonSF))[,2]))
}
#this function convert a raster to a data frame so it can be plotted in ggplot
rast <- function(inRaster) {
data.frame(
xyFromCell(inRaster, 1:ncell(inRaster)),
value = getValues(inRaster)) }
#this function aggregates rasters
aggregateRaster <- function(inputRasterList, AtlantaMSA_fishnet) {
#create an empty fishnet with the same dimensions as the input fishnet
theseFishnets <- AtlantaMSA_fishnet %>% dplyr::select()
#for each raster in the raster list
for (i in inputRasterList) {
#create a variable name corresponding to the ith raster
varName <- names(i)
#convert raster to points as an sf
thesePoints <-
rasterToPoints(i) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(AtlantaMSA_fishnet)) %>%
filter(.[[1]] == 1)
#aggregate to the fishnet
thisFishnet <-
aggregate(thesePoints, AtlantaMSA_fishnet, length) %>%
mutate(!!varName := ifelse(is.na(.[[1]]),0,1))
#add to the larger fishnet
theseFishnets <- cbind(theseFishnets, thisFishnet)
}
#output all aggregates as one large fishnet
return(theseFishnets)
}
Introduction: This Model and Its Significance to Planners
Welcome! In this interactive R-markdown memo, we employ historical
patterns of development and population trends in the Atlanta, Georgia
metropolitan statistical area (“MSA”) to predict future development
scenarios. Specifically, we examine the spatial relationships that exist
among population growth and highway infrastructure with land
development. Through these models, we explore the relationships between
each of these independent variables and urban sprawl, our dependent
variable.
Urban sprawl is a development scenario characterized by “leap
frogging” development, a development scenario that disproportionately
consumes land and environmental resources, with significant negative
ecological externalities. These models will predict likely urban sprawl
for the Atlanta metro area based on two scenarios. The models presented
here enable planners and policy makers to preemptively develop policies
to calibrate against urban sprawl, empowering municipal governments to
create conscientious land allocation plans.
The model developed will predict two different scenarios: demand-side
and supply-side. In the demand-side model, we attempt to understand the
relationship that population growth has with urban sprawl in the MSA.
Specifically, we predict the demand for development according to
population growth, and how this would spatially manifest based on
existing land use trends and population projections. The supply-side
model will predict how future development might occur following the
construction of a new, theoretical highway, supplying infrastructure.
Through this scenario, we attempt to ascertain the relationship that
highway development has with urban sprawl.
Let’s explore how sprawl can occur, unmitigated, based on these
independent variables. We use land cover and population data from the
years 2008/2009 and 2019 to predict for the year 2029.
Region of interest: Atlanta, GA metro
Years of study: 2008-2019
Projecting to 2029
Data level: fishnet grid cell
The following chunks of code will collect the prerequisite data
needed for the model from the two years of study, structure the data in
preparation for modeling, and run the demand - and supply- side models
based on the study years’ data to project for the year 2030.
Data Gathering and Feature Engineering
First, we pull in and visualize our spatial datasets of the Atlanta
MSA’s 21 counties.
And retrieve the Atlanta MSA boundary using the tigris
package.
#plot MSA boundary to make sure its right
ggplot() +
geom_sf(data = metro_counties) +
geom_sf(data = Atlanta_MSA,
color = "black", fill = "transparent", linewidth = .75) +
mapTheme +
labs(title = "Atlanta 21 County MSA")

We then create a fishnet of the Atlanta MSA using 1000’ x 1000’
cells. A fishnet is a vector data set that will allow us to attach other
information to the forthcoming raster data set on land cover.
AtlantaMSA_fishnet <-
st_make_grid(metro_counties, cellsize = 1000, square = TRUE) %>%
st_sf() %>%
st_transform("ESRI:102267")
AtlantaMSA_fishnet <-
AtlantaMSA_fishnet[metro_counties,]
#plot MSA boundary to make sure its right
ggplot()+
geom_sf(data = AtlantaMSA_fishnet,
fill = "lightgrey") +
geom_sf(data = Atlanta_MSA,
color = "black", fill = "transparent", linewidth = .75) +
labs(title = "Atlanta MSA Fishnet") +
mapTheme

Land Cover
Land Cover by Year and Type
We retrieve the raster files of land cover for the Atlanta MSA. We
downloaded these datasets from the Multi-Resolution Land Characteristics
Consortium’s National Land Cover Database, and clipped them in ArcGIS to
a rectangular boundary extent that aligned with the farthest points of
the Atlanta region’s 21 county border.
##SKIP##
#load full size rasters
lc_2008 = raster("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/56bd35b343d39e88bd8e1fdc89957e6ccb804fe9/Data/raster/2008LC_proj.tif")
lc_2019 = raster("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/56bd35b343d39e88bd8e1fdc89957e6ccb804fe9/Data/raster/2019LC_proj.tif")
The raster files were still very large, so we down-sampled them to
make their size more manageable and then clipped them precisely to the
MSA boundary.
## SKIP ##
#down sample the raster to make more manageable
lc_2008 <- aggregate(lc_2008, fact = 7, fun = modal)
lc_2019 <- aggregate(lc_2019, fact = 7, fun = modal)
#crop rasters
lc_2008_crop <- crop(lc_2008, extent(metro_counties))
lc_2019_crop <- crop(lc_2019, extent(metro_counties))
#mask rasters
lc_2008_crop <- mask(x = lc_2008_crop, mask = metro_counties)
lc_2019_crop <- mask(x = lc_2019_crop, mask = metro_counties)
#Export downsized rasters
#writeRaster(lc_2008_crop, filename=file.path(rast_dir, "lc2008_sml.tif"), format="GTiff", overwrite=TRUE)
#writeRaster(lc_2019_crop, filename=file.path("C:/Users/ctown/OneDrive - PennO365/Classes/Classes_Sem4_2023Spring/CPLN 675_Land Modeling/Assignments/A5_SprawlForecasting/LUEM_A5_Repo/Data/raster", "lc2019_sml.tif"), format="GTiff", overwrite=TRUE)
lc_2008 <- raster("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/24f08e61c5ec983b3fa39f1f3b03fb90aa3a13ee/Data/raster/lc2008_sml.tif")
lc_2019 <- raster("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/24f08e61c5ec983b3fa39f1f3b03fb90aa3a13ee/Data/raster/lc2019_sml.tif")
This allows us to visualize Atlanta’s land cover in 2008 and
2019.
grid.arrange(ncol = 1,
ggplot() +
geom_raster(data=rast(lc_2008) %>% na.omit %>% filter(value > 0),
aes(x,y,fill=as.factor(value))) +
scale_fill_viridis(discrete=TRUE,
name ="",
option = "C") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .5) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .75) +
labs(title = "Land Cover, 2008") +
mapTheme +
theme(legend.direction="horizontal"),
ggplot() +
geom_raster(data=rast(lc_2019) %>% na.omit %>% filter(value > 0),
aes(x, y, fill=as.factor(value))) +
scale_fill_viridis(discrete=TRUE,
name ="",
option = "C") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .5) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .75) +
labs(title = "Land Cover, 2019") +
mapTheme +
theme(legend.direction="horizontal")
)

Viewing land cover at the MSA level in 2008 and 2019 shows that
change is happening at too small a scale to discern visually at this
extent. Therefore, we summon the powers of R. First, we distill the many
specific land cover classifications into six simple buckets: developed
land, forest, farmland, wetlands, water bodies, and other undeveloped
lands.
Land Cover by Type
developed08 <- lc_2008 == 21 | lc_2008 == 22 | lc_2008 == 23 | lc_2008 == 24
forest08 <- lc_2008 == 41 | lc_2008 == 42 | lc_2008 == 43
farm08 <- lc_2008 == 81 | lc_2008 == 82
wetlands08 <- lc_2008 == 90 | lc_2008 == 95
otherUndeveloped08 <- lc_2008 == 52 | lc_2008 == 71 | lc_2008 == 31
water08 <- lc_2008 == 11
developed19 <- lc_2019 == 21 | lc_2019 == 22 | lc_2019 == 23 | lc_2019 == 24
forest19 <- lc_2019 == 41 | lc_2019 == 42 | lc_2019 == 43
farm19 <- lc_2019 == 81 | lc_2019 == 82
wetlands19 <- lc_2019 == 90 | lc_2019 == 95
otherUndeveloped19 <- lc_2019 == 52 | lc_2019 == 71 | lc_2019 == 31
water19 <- lc_2019 == 11
names(developed08) <- "developed08"
names(forest08) <- "forest08"
names(farm08) <- "farm08"
names(wetlands08) <- "wetlands08"
names(otherUndeveloped08) <- "otherUndeveloped08"
names(water08) <- "water08"
names(developed19) <- "developed19"
names(forest19) <- "forest19"
names(farm19) <- "farm19"
names(wetlands19) <- "wetlands19"
names(otherUndeveloped19) <- "otherUndeveloped19"
names(water19) <- "water19"
This distilled, categorical dataset for each year is then aggregated
to our fishnet of the Atlanta MSA. The resulting maps show that Atlanta
is a heavily forested area. Development is vast, with its anchor in the
center of the MSA, radiating outward. A significant amount of farmland
exists in a thick band against the MSA’s periphery. Undeveloped areas
are scattered, also mostly near the MSA’s periphery.
theRasterList08 <- c(developed08, forest08, farm08, wetlands08, otherUndeveloped08, water08)
theRasterList19 <- c(developed19, forest19, farm19, wetlands19, otherUndeveloped19, water19)
aggregatedRasters08 <-
aggregateRaster(theRasterList08, AtlantaMSA_fishnet) %>%
dplyr::select(developed08, forest08, farm08, wetlands08, otherUndeveloped08, water08) %>%
mutate_if(is.numeric, as.factor)
aggregatedRasters19 <-
aggregateRaster(theRasterList19, AtlantaMSA_fishnet) %>%
dplyr::select(developed19, forest19, farm19, wetlands19, otherUndeveloped19, water19) %>%
mutate_if(is.numeric, as.factor)
rasters08_map <- aggregatedRasters08 %>%
gather(var,value,developed08:water08) %>%
st_cast("POLYGON") %>% #just to make sure no weird geometries slipped in
mutate(X = xyC(.)$x,
Y = xyC(.)$y) %>%
ggplot() +
geom_sf(data=metro_counties) +
geom_point(aes(X,Y, colour=as.factor(value))) +
facet_wrap(~var) +
scale_colour_manual(values = c(gray, highlight),
labels=c("False","True"),
name = "") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .25) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .5) +
labs(title = "Land Cover Types, 2008",
subtitle = "As fishnet centroids") +
mapTheme +
theme() +
theme(legend.position = "none")
rasters08_map

Land Cover Change
To identify areas with land cover change, we use the raster data sets
and reclassify the land cover types as numerical values, where developed
lands have the value of 1 and undeveloped lands have the value of 0, for
each of the years’ data sets. We are then able to add these data sets
together on a cell-by-cell basis to understand where development has
occurred in 2019 and where it was not in 2008. When added together, the
cells with values of 1 represent where new development has occurred; a
value of 2 shows where development was in 2008 and remained in 2019, and
a value of 0 shows land that was and remains undeveloped. This function
is like using raster calculator in GIS.
#creat matrix for reclassifying land cover
reclassMatrix <-
matrix(c(
0,12,0,
12,24,1,
24,Inf,0),
ncol=3, byrow=T)
reclassMatrix
#reclassify land cover change using reclass matrix
lc_2008r <-
reclassify(lc_2008, reclassMatrix)
lc_2019r <-
reclassify(lc_2019, reclassMatrix)
lc_2008r[lc_2008r < 1] <- 0
names(lc_2008r) <- "dev08"
lc_2019r[lc_2019r < 1] <- 0
names(lc_2019r) <- "dev19"
development_change <- lc_2008r + lc_2019r
# convert the raster to a vector
dev_change_vec <- as.vector(development_change)
# create the histogram plot with ggplot2
ggplot(data.frame(value = dev_change_vec), aes(x=value)) +
geom_histogram(binwidth=.5, fill=highlight, color="white") +
labs(title="Development Change by Type (2008-2019)",
subtitle = "0 = Remained Undeveloped, 1 = New Development, 2 = Already Developed",
x="Development Change",
y="Frequency") +
plotTheme

This shows us that most cells in the MSA remained undeveloped from
2008 to 2019, many of them were already developed, and a few of the
previously undeveloped cells converted to developed. We can then
visualize where the new development cells with the 1 value are, which
shows that new development has been scattered throughout the MSA.
development_change[development_change != 1] <- NA
ggplot() +
geom_sf(data=Atlanta_MSA) +
geom_raster(data=rast(development_change) %>% na.omit,
aes(x,y,fill=as.factor(value))) +
scale_fill_viridis(discrete=TRUE,
option = "C",
name ="Cells \nwith Land Cover\nChange") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .5) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .75) +
labs(title="Development Land Cover Change (2008-2019)",
subtitle = "Atlanta metro area | Raster cells") +
mapTheme +
theme(legend.position = "none")

We then join this dataset, mapping new development, to our fishnet.
This has the effect of magnifying where the development change has
happened a bit.
dev_change <- reclassify(development_change, reclassMatrix)
dev_change[development_change < 1] <- NA
names(dev_change) <- "development_change"
changePoints <-
rasterToPoints(dev_change) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(AtlantaMSA_fishnet))
dev_change_fishnet <-
aggregate(changePoints, AtlantaMSA_fishnet, sum) %>%
mutate(development_change = ifelse(is.na(development_change),0,1),
development_change = as.factor(development_change))
ggplot() +
geom_point(data=dev_change_fishnet,
aes(x=xyC(dev_change_fishnet)$x, y=xyC(dev_change_fishnet)$y, colour=development_change)) +
scale_colour_manual(values = c(gray, highlight),
labels=c("No Change","New Development"),
name = "") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .5) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .75) +
labs(title = "New Development (2019)", subtitle = "Atlanta metro area | Fishnet centroids") +
mapTheme

#dev_change_fishnet <- st_write(dev_change_fishnet, "C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Final/LUEM_A5_Repo/Data/geojson/dev_change_fishnet.geojson")
Census Data: Population
Next, we set up our MSA population data, which will prepare us for
the demand-side model. The next two chunks download the population data
for 2009 and 2019 from the American Community Survey 5-year Estimates on
a census tract level.
We then visualize the population for the two years by the respective
census tract boundaries for each of the years.
grid.arrange(
ggplot() +
geom_sf(data = Pop09, aes(fill=factor(ntile(pop09,5))), colour=NA) +
geom_sf(data = Atlanta_MSA,
color = "black", fill = "transparent", linewidth = .75) +
scale_fill_manual(values = palette5,
labels=quintileBreaks(Pop09,"pop09"),
name="Quintile\nBreaks") +
labs(title="2009 Population\nAtlanta 21-County MSA",
subtitle="By Census Tract") +
mapTheme,
ggplot() +
geom_sf(data = Pop19, aes(fill=factor(ntile(pop19,5))), colour=NA) +
scale_fill_manual(values = palette5,
labels=quintileBreaks(Pop19,"pop19"),
name="Quintile\nBreaks") +
geom_sf(data = Atlanta_MSA,
color = "black", fill = "transparent", linewidth = .75) +
labs(title="2019 Population\nAtlanta 21-County MSA",
subtitle="By Census Tract") +
mapTheme, ncol=2)

We then reconcile the tract boundaries with the fishnet grid cells to
be able to conduct analysis and build models with our fishnet data
set.
### Breaking here on select(fishnetID)
AtlantaMSA_fishnet <-
AtlantaMSA_fishnet %>%
rownames_to_column("fishnetID") %>%
mutate(fishnetID = as.numeric(fishnetID)) %>%
dplyr::select(fishnetID)
fishnetPop09 <-
st_interpolate_aw(Pop09["pop09"], AtlantaMSA_fishnet, extensive=TRUE) %>%
as.data.frame(.) %>%
rownames_to_column(var = "fishnetID") %>%
left_join(AtlantaMSA_fishnet %>%
mutate(fishnetID = as.character(fishnetID)),
., by=c("fishnetID"='fishnetID')) %>%
mutate(pop09 = replace_na(pop09,0)) %>%
dplyr::select(pop09)
fishnetPop19 <-
st_interpolate_aw(Pop19["pop19"], AtlantaMSA_fishnet, extensive=TRUE) %>%
as.data.frame(.) %>%
rownames_to_column(var = "fishnetID") %>%
left_join(AtlantaMSA_fishnet %>%
mutate(fishnetID = as.character(fishnetID)),
., by=c("fishnetID"='fishnetID')) %>%
mutate(pop19 = replace_na(pop19,0)) %>%
dplyr::select(pop19)
fishnetPop <-
cbind(fishnetPop09, fishnetPop19) %>%
dplyr::select(pop09,pop19) %>%
mutate(pop_Change = pop19 - pop09)
We can then compare population by tract vs population by grid
cell.
grid.arrange(
ggplot() +
geom_sf(data=Pop19, aes(fill=factor(ntile(pop19,5))),colour=NA) +
scale_fill_manual(values = palette5,
labels=substr(quintileBreaks(Pop19,"pop19"),1,4),
name="Quintile\nBreaks") +
geom_sf(data = Atlanta_MSA,
color = "black", fill = "transparent", linewidth = .75) +
labs(title="2019 Population\nAtlanta 21-County MSA",
subtitle="Represented as tracts; Boundaries omitted") +
mapTheme,
ggplot() +
geom_sf(data=fishnetPop, aes(fill=factor(ntile(pop19,5))), colour=NA) +
scale_fill_manual(values = palette5,
labels=substr(quintileBreaks(fishnetPop,"pop19"),1,4),
name="Quintile\nBreaks") +
geom_sf(data = Atlanta_MSA,
color = "black", fill = "transparent", linewidth = .75) +
labs(title="2019 Population\nAtlanta 21-County MSA",
subtitle="Represented as fishnet gridcells; Boundaries omitted") +
mapTheme, ncol=2)

Highway Distance
Next, we prepare our supply-side model by loading in spatial data for
Atlanta’s existing highways.
## Reading layer `Major_Roads' from data source
## `https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/8b689651286bb4ff28554095bd39ce1bd87655e4/Data/geojson/Major_Roads.geojson'
## using driver `GeoJSON'
## Simple feature collection with 2669 features and 6 fields
## Geometry type: LINESTRING
## Dimension: XY
## Bounding box: xmin: -85.35708 ymin: 32.84459 xmax: -83.50588 ymax: 34.57842
## Geodetic CRS: WGS 84

This suggests there may be an association between proximity to
highways and new development. To better understand this relationship, we
calculate how far each fishnet cell is from a highway. We do this by
converting the highway layer to raster, converting the raster to points,
and then calculating the mean distance from a highway for each grid
cell.
#SKIP#
#create a raster layer of highways
highway_raster <-
as(AtlantaHighways,'Spatial') %>%
rasterize(.,emptyRaster)
#Export rasters
rast_dir = paste(dir_ct, 'raster', sep='/')
#writeRaster(highway_raster, filename=file.path(rast_dir, "highway_raster.tif"), format="GTiff", overwrite=TRUE)
This visualizes each cell’s distance from a highway.

Calculating the Spatial Lag of Development
We measure accessibility by way of a spatial lag, hypothesizing that
new development is a function of distance to existing development. The
shorter the distance, the more accessible a grid cell is to existing
development. We measure this by calculating the average distance from
each grid cell to its 2 nearest developed neighboring grid cells in
2009.
nn_function <- function(measureFrom,measureTo,k) {
#convert the sf layers to matrices
measureFrom_Matrix <-
as.matrix(measureFrom)
measureTo_Matrix <-
as.matrix(measureTo)
nn <-
get.knnx(measureTo, measureFrom, k)$nn.dist
output <-
as.data.frame(nn) %>%
rownames_to_column(var = "thisPoint") %>%
gather(points, point_distance, V1:ncol(.)) %>%
arrange(as.numeric(thisPoint)) %>%
group_by(thisPoint) %>%
summarize(pointDistance = mean(point_distance)) %>%
arrange(as.numeric(thisPoint)) %>%
dplyr::select(-thisPoint) %>%
pull()
return(output)
}
AtlantaMSA_fishnet$lagDevelopment08 <-
nn_function(xyC(AtlantaMSA_fishnet),
xyC(filter(aggregatedRasters08, developed08==1)),
2)
# compute natural breaks for "lagDevelopment08"
breaks <- classIntervals(AtlantaMSA_fishnet$lagDevelopment08, n=4, style="jenks")
#Plot
ggplot() +
geom_sf(data=metro_counties) +
geom_point(data=AtlantaMSA_fishnet,
aes(x=xyC(AtlantaMSA_fishnet)[,1], y=xyC(AtlantaMSA_fishnet)[,2],
colour=cut(lagDevelopment08, breaks$brks)), size=1.5) +
scale_colour_manual(values=palette5,
labels=format(breaks$brks, nsmall=1),
name="Distance (ft)") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .5) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .75) +
labs(title = "Spatial Lag to 2008 Development",
subtitle = "As fishnet centroids") +
mapTheme

This shows most of the area in the MSA’s inner counties was near
existing development in 2008. It was only the counties at the outer
fringes that still had a few areas relatively distant from
development.
Create Final Dataset
We then aggregate all these data to a final fishnet data set for
feeding into our model. This fishnet includes columns which indicate,
cell-by-cell, if there has been development change, land use type, the
cell’s distance to highways, the cell’s distance to existing
development, and its population in both years.
dat <-
cbind(
AtlantaMSA_fishnet, dev_change_fishnet, highwayPoints_fishnet, fishnetPop, aggregatedRasters08) %>%
dplyr::select(development_change, developed08, forest08, farm08, wetlands08, otherUndeveloped08, water08,
pop09, pop19, pop_Change, distance_highways, lagDevelopment08) %>%
st_join(metro_counties) %>%
mutate(developed19 = ifelse(development_change == 1 & developed08 == 1, 0, 1)) %>% #charlie to confirm if the else value should be 1 or 'developed'
filter(water08 == 0)
Data Exploration
Now that all of our data are assembled, we explore how each factor
uncovered thus far is related to new development. This plot displays
distance to highways and the spatial lag of development as continuous
variables. This shows us that new development has been, on average,
closer to highways, and closer to existing development in aggregate,
than cells that had no change in land cover. This supports our previous
hypothesis that new development might be occurring closer to highways
and to previously developed areas.
dat %>%
dplyr::select(distance_highways,lagDevelopment08,development_change) %>%
gather(Variable, Value, -development_change, -geometry) %>%
ggplot(., aes(development_change, Value, fill=development_change)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of the Continuous Variables") +
plotTheme

This next plot shows that new development has, on average, occurred
in cells where the population is greater or growing. From this, we learn
that development in Atlanta MSA is also positively correlated with the
amount of population and population growth in an area.
dat %>%
dplyr::select(pop09,pop19,pop_Change,development_change) %>%
gather(Variable, Value, -development_change, -geometry) %>%
ggplot(., aes(development_change, Value, fill=development_change)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of Factor Variables") +
plotTheme

From the above data exploration, we can see that development is
positively correlated with proximity to highways and development, as
well as with existing and growing population. However, this leaves us
wondering which types of land cover are most likely to convert to
developed land?
The conversion table shows us that forested areas in 2008 were the
most likely to have been developed by 2019. This shows that Atlanta has
a preference for developing woodlands first, with areas adjacent to
those that are already developed as a close second.
dat %>%
dplyr::select(development_change:otherUndeveloped08,developed08) %>%
gather(Land_Cover_Type, Value, -development_change, -geometry) %>%
st_set_geometry(NULL) %>%
group_by(development_change, Land_Cover_Type) %>%
summarize(n = sum(as.numeric(Value))) %>%
ungroup() %>%
mutate(Conversion_Rate = paste0(round(100 * n/sum(n), 2), "%")) %>%
filter(development_change == 1) %>%
dplyr::select(Land_Cover_Type,Conversion_Rate) %>%
reactable(theme = reactableTheme(style = list(fontFamily = "Arial, sans-serif", fontSize = "1.0rem")))
Binary Logistic Regression - Predicting for 2019
Now that we have gathered a number of factors we hypothesized to be
related to new development, and have proved that they have at least some
association with development, we can build and train a model to predict
new development based on them.
We chose to use binomial logistic regression for this assignment
because it is a type of statistical model that is well suited to
analyzing relationships between a binary response variable and one or
more predictor variables. It estimates the probability of the response
variable taking one of two possible values (in this case developed or
not developed) based on the values of the predictor variables.
To predict the likelihood of future development, we use data from
2008/2009 to create a series of logistic regression models to predict
land use change in 2019. We then evaluate the performance of these
models to select our best model for predicting development in 2029.
Model Building
The first step of this process is randomly splitting the data in half
to create a testing data set with one half and a training data set with
the other.
set.seed(3456)
trainIndex <-
createDataPartition(dat$developed08, p = .50,
list = FALSE,
times = 1)
datTrain <- dat[ trainIndex,]
datTest <- dat[-trainIndex,]
We then build a series of binary logistic regression models based on
our independent variables.
The six models we created are numbered to reflect increasing
complexity. While Model 1 concentrates exclusively on land cover type
and whether development change occurred between the two time periods,
later models incorporate the spatial lag of development, population
elements, and our distance variables.
Model1 <- glm(development_change ~ wetlands08 + forest08 + farm08 + otherUndeveloped08,
family="binomial"(link="logit"), data = datTrain)
Model2 <- glm(development_change ~ wetlands08 + forest08 + farm08 + otherUndeveloped08 + lagDevelopment08,
family="binomial"(link="logit"), data = datTrain)
Model3 <- glm(development_change ~ wetlands08 + forest08 + farm08 + otherUndeveloped08 + lagDevelopment08 + pop09,
family="binomial"(link="logit"), data = datTrain)
Model4 <- glm(development_change ~ wetlands08 + forest08 + farm08 + otherUndeveloped08 + lagDevelopment08 + pop09,
family="binomial"(link="logit"), data = datTrain)
Model5 <- glm(development_change ~ wetlands08 + forest08 + farm08 + otherUndeveloped08 + lagDevelopment08 + pop09 + pop_Change,
family="binomial"(link="logit"), data = datTrain)
Model6 <- glm(development_change ~ wetlands08 + forest08 + farm08 + otherUndeveloped08 + lagDevelopment08 + pop_Change + pop09 + distance_highways,
family="binomial"(link="logit"), data = datTrain)
modelList <- paste0("Model", 1:6)
map_dfc(modelList, function(x)pR2(get(x)))[4,] %>%
setNames(paste0("Model",1:6)) %>%
gather(Model,McFadden) %>%
ggplot(aes(Model,McFadden)) +
geom_bar(stat="identity", fill = highlight) +
labs(title= "McFadden R-Squared by Model") +
plotTheme
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2

We then examine the McFadden R-Squared of the six models to select
our best final model. Model 1 clearly performs more poorly than the
others. However, models 2-4 also perform moderately less well than the
remaining models. Therefore, we eliminate models 1-4 and choose Model 6
as our final model because it performs the best and allows us to explore
the effects of all of our variables of interest.
We then use the predicted values from Model 6 to create a histogram
of the model’s test set predicted probabilities. This shows us that a
high density of cells have roughly a 40% likelihood of development. This
makes sense given the sprawling nature of the Atlanta MSA’s existing
development patterns, which are acreage intensive. It also makes sense
given the map of new development we plotted earlier, in which it seems
feasible that roughly ~30-40% of the MSA was covered by new development
in 2019.
testSetProbs <-
data.frame(class = datTest$development_change,
probs = predict(Model6, datTest, type="response"))
ggplot(testSetProbs, aes(probs)) +
geom_density(aes(fill=class), alpha=0.75) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "Histogram of test set predicted probabilities",
x="Predicted Probabilities",y="Density") +
plotTheme

Accuracy
This next segment tests our model’s:
- Sensitivity: the extent to which it accurately predicts new
development, or “true positives”, and:
- Specificity, the extent to which it accurately predicts “true
negatives.”
Our ultimate goal with a land cover change model is to optimize the
balance between the model’s predictions with regard to both sensitivity
and specificity. Meaning, we want the model to be capable of predicting
where development occurs so that we can plan for those areas, but we
also want it to accurately predict where development would not occur, to
inform our other land use planning.
At the first two thresholds specified below, 5% and 17%, we can see
that the 5% threshold very accurately predicts the “true negatives,”
where development does not occur. The threshold of 17%, however,
performs better on the “true positive” rate of predicted v. actual
development, that has occurred. We found, however, that a threshold of
around 34% gives the best balance between the trade-offs of sensitivity
and specificity. We chose 34% because it gives us the highest overall
accuracy, and allows us to keep both our sensitivity and specificity
over 50%.
options(yardstick.event_first = FALSE)
testSetProbs <-
testSetProbs %>%
mutate(predClass_05 = as.factor(ifelse(testSetProbs$probs >= 0.05 ,1,0)),
predClass_17 = as.factor(ifelse(testSetProbs$probs >= 0.17 ,1,0)),
predClass_34 = as.factor(ifelse(testSetProbs$probs >= 0.34 ,1,0)))
testSetProbs %>%
dplyr::select(-probs) %>%
gather(Variable, Value, -class) %>%
group_by(Variable) %>%
summarize(Sensitivity = round(yardstick::sens_vec(class,factor(Value)),2),
Specificity = round(yardstick::spec_vec(class,factor(Value)),2),
Accuracy = round(yardstick::accuracy_vec(class,factor(Value)),2)) %>%
reactable(theme = reactableTheme(style = list(fontFamily = "Arial, sans-serif", fontSize = "1.0rem")))
We then convert these true/false positive/negative indicators to
factors in order to map them.
predsForMap <-
dat %>%
mutate(probs = predict(Model6, dat, type="response") ,
Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_17_Pct = as.factor(ifelse(probs >= 0.17 ,1,0)),
Threshold_34_Pct = as.factor(ifelse(probs >= 0.34 ,1,0))) %>%
dplyr::select(development_change,Threshold_5_Pct,Threshold_17_Pct, Threshold_34_Pct) %>%
gather(Variable,Value, -geometry) %>%
st_cast("POLYGON")
ggplot() +
geom_point(data=predsForMap, aes(x=xyC(predsForMap)[,1], y=xyC(predsForMap)[,2], colour=Value)) +
facet_wrap(~Variable) +
scale_colour_manual(values = c(gray, highlight), labels=c("No Change","New Development"),
name="") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .5) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .75) +
labs(title="Development Predictions - Low Threshold") +
mapTheme +
theme(legend.position = "bottom")

These maps show that the 34% nicely captures the MSA’s overall
pattern of development while minimizing the tendency to over-predict
development that the 17% and 5% thresholds share (the 5% threshold
wildly so).
To better understand the trade-offs between accurately predicting
false positives and false negatives, we can run our predictions through
a confusion matrix to visualize the true positives (“TrueP”) and true
negatives (“TrueN”) in the charts below, for each of the thresholds.
dat <- dat %>%
mutate(probs = predict(Model6, dat, type="response"))
ConfusionMatrix.metrics <-
dat %>%
mutate(Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_17_Pct = as.factor(ifelse(probs >= 0.17 ,1,0)),
Threshold_34_Pct = as.factor(ifelse(probs >= 0.34 ,1,0))) %>%
mutate(TrueP_05 = ifelse(development_change == 1 & Threshold_5_Pct == 1, 1,0),
TrueN_05 = ifelse(development_change == 0 & Threshold_5_Pct == 0, 1,0),
TrueP_17 = ifelse(development_change == 1 & Threshold_17_Pct == 1, 1,0),
TrueN_17 = ifelse(development_change == 0 & Threshold_17_Pct == 0, 1,0),
TrueP_34 = ifelse(development_change == 1 & Threshold_34_Pct == 1, 1,0),
TrueN_34 = ifelse(development_change == 0 & Threshold_34_Pct == 0, 1,0)) %>%
dplyr::select(., starts_with("True")) %>%
gather(Variable, Value, -geometry) %>%
st_cast("POLYGON")
ggplot(data=ConfusionMatrix.metrics) +
geom_point(aes(x=xyC(ConfusionMatrix.metrics)[,1],
y=xyC(ConfusionMatrix.metrics)[,2], colour = as.factor(Value))) +
facet_wrap(~Variable) +
scale_colour_manual(values = c(gray, highlight),
labels=c("Incorrect","Correct"),
name="") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .5) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .75) +
labs(title="Development Predictions - Low Threshold") +
mapTheme +
theme(legend.position = "bottom")

Mapping the sensitivity (true positives) and specificity (true
negatives) of these thresholds shows in further detail that the 34%
threshold predicts noticeably true negatives than the other thresholds
while predicting only slightly less true positives. Therefore, we select
34% as the probability threshold at which to classify land as likely to
be developed in the remainder of this project.
Planning Scenarios
Now that we have developed a predictive model for new development, we
can use it to test the effects of potential future scenarios. We first
use the model to predict how a demand-side change (projected population)
might affect development in 2029. We then use the model to predict how a
supply-side change (a new highway) might affect development
Scenario 1: Demand-side Change Forecast
In this demand-side projection, we will use population projections
for the Atlanta MSA counties and distribute the population among our
fishnet cells. The goal is to visualize change and plan for growth
scenarios based on how anticipated future population might drive
development.
First, we replace the lag development 2008 column with the distance
from development in 2019.
dat <-
dat %>%
mutate(lagDevelopment08 = nn_function(xyC(.), xyC(filter(., developed19 == 1)),2))
Next, we look at projected population by county in 2029 based on
population estimates from the Georgia Data Analytics
Center.
#County projections 2029
#Barrow 96977 -
#Bartow 119594 -
#Carroll 131577 -
#Cherokee 301752-
#Clayton 321410-
#Cobb 830671-
#Coweta 172341 -
#Dawson 32572-
#DeKalb 822154-
#Douglas 160635-
#Fayette 128627-
#Forsyth 316165-
#Fulton 1198334-
#Gwinnett 1044026-
#Hall 232720-
#Henry 278739-
#Newton 130559-
#Paulding 208196-
#Rockdale 95574-
#Spalding 70974-
#Walton 109205-
countyPopulation_2029 <-
data.frame(
NAME = Counties,
county_projection_2029 =
c(96977,119594,131577,301752,321410,830671,172341,32572,822154,160635,128627,
316165,1198334,1044026,232720,278739,130559,208196,95574,70974,109205)) %>%
left_join(
dat %>%
st_set_geometry(NULL) %>%
group_by(NAME) %>%
summarize(county_population_2019 = round(sum(pop19))))
countyPopulation_2029 %>%
gather(Variable,Value, -NAME) %>%
ggplot(aes(reorder(NAME,-Value),Value)) +
geom_bar(aes(fill=Variable), stat = "identity", position = "dodge") +
scale_fill_manual(values = palette2,
labels=c("2019","2029"),
name="Population") +
labs(title="Population Change by County: 2019 - 2029",
x="County", y="Population") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
plotTheme

We see that Fulton, Gwinnett, DeKalb, and Cobb counties are projected
to experience the largest population increases between 2019 and
2029.
Predicting Development Demand (based on projected population)
Now, we will take the county-level population projections and
distribute them across the study area. We do this by weighting it
proportionally according to a grid cell’s 2019 population and then
subtracting 2019 pop to get pop_change.
dat_infill <-
dat %>%
#calculate population change
left_join(countyPopulation_2029) %>%
mutate(proportion_of_county_pop = pop19 / county_population_2019,
pop_2029.infill = proportion_of_county_pop * county_projection_2029,
pop_Change = round(pop_2029.infill - pop19),2) %>%
dplyr::select(-county_projection_2029, -county_population_2019,
-proportion_of_county_pop, -pop_2029.infill) %>%
#predict for 2029
mutate(predict_2029.infill = predict(Model6,. , type="response"))
map_pred29 <-
dat_infill %>%
ggplot() +
geom_point(aes(x=xyC(dat_infill)[,1], y=xyC(dat_infill)[,2], colour = factor(ntile(predict_2029.infill,5)))) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(dat_infill,"predict_2029.infill"),1,4),
name="% Likelihood\nof Development") +
geom_sf(data=metro_counties, fill=NA, colour="white", size=.75) +
geom_sf(data = Atlanta_MSA, fill = "transparent",
color = "black", linewidth = .75) +
labs(title= "Development Demand in 2029: Predicted Probabilities",
subtitle = "Based on population change projections")+
mapTheme
map_pred29

Mapping predicted development based on projected population change
for 2029 doesn’t show any huge changes from where our model predicted
development would be in 2019. New development is still predicted to most
likely occur in the outer counties. Although, our model is predicting
increased development in the northwest and southwest corners of the
metro which it was not predicting before we added in the 2029 population
projections.
Scenario 2: Supply-side Change Forecast
To begin our supply-side projection, we imagine a new highway in
Walton County, in the eastern part of the metro, where development
between 2008 and 2019 was less concentrated. This was also a forested
area, so the fact that much development had not happened there over the
earlier period aroused our curiosity. This way, we can test the extent
that a new road investment influences development in a desirable area of
land cover. This new road was created as a line segment in ArcGIS and
merged with the existing highways shapefile.
#NEW HIGHWAY SEGMENT ONLY
new_highway <- st_read("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/a5a5b0c9099b97f68c0d1aeadcdfe9dbd2960a9a/Data/geojson/new_highway.geojson")
new_highway <- new_highway %>%
st_transform(st_crs(metro_counties)) %>%
st_intersection(metro_counties)
#new_highway <- st_write(new_highway,"C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Final/LUEM_A5_Repo/Data/geojson/new_highway.geojson")
#ALL HIGHWAYS MERGED TOGETHER -- highways_all --
highways_all <- st_read("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/a5a5b0c9099b97f68c0d1aeadcdfe9dbd2960a9a/Data/geojson/highways_all.geojson") %>%
st_transform(st_crs(metro_counties)) %>%
st_intersection(metro_counties)
ggplot() +
geom_point(data=dev_change_fishnet,
aes(x=xyC(dev_change_fishnet)$x, y=xyC(dev_change_fishnet)$y, colour=development_change)) +
scale_colour_manual(values = c(gray, "#ED7953FF"),
labels=c("No Change","New Development"),
name = "") +
labs(title = "Land Cover Development Change",
subtitle = "Atlanta metro area | Fishnet centroids with existing highways\nin dark blue and a new highway in cyan") +
geom_sf(data=metro_counties, fill=NA, colour="white", size=.75) +
geom_sf(data = Atlanta_MSA, fill = "transparent",
color = "black", linewidth = .75) +
geom_sf(data=AtlantaHighways, color = highlight, linewidth = .5) +
geom_sf(data=new_highway, colour="cyan", linewidth=1) +
mapTheme

Now, with the new highways dataset, we can create a new
distance-to-highway raster, and from there convert it to fishnet
centroids which we merge with the fishnet. This helps us understand how
far the fishnet cells are from the new highway.
#new_highway_raster <-
# as(highways_all,'Spatial') %>%
# rasterize(.,emptyRaster)
#new_highway_raster_distance <- distance(new_highway_raster)
#writeRaster(new_highway_raster_distance, filename=file.path(rast_dir, "new_highway_dist.tif"), format="GTiff", overwrite=TRUE)
new_highway_raster_distance <- raster(paste(dir_git, "187ac7cf231257fc52de0f7f2a1dc2014e4e6f2d/Data/raster/new_highway_dist.tif", sep='/'))
names(new_highway_raster_distance) <- "distance_highways"
new_highwayPoints <-
rasterToPoints(new_highway_raster_distance) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(AtlantaMSA_fishnet))
new_highwayPoints_fishnet <-
aggregate(new_highwayPoints, AtlantaMSA_fishnet, mean) %>%
mutate(distance_highways = ifelse(is.na(distance_highways),0,distance_highways))
ggplot() +
geom_sf(data=metro_counties) +
geom_point(data=new_highwayPoints_fishnet, aes(x=xyC(new_highwayPoints_fishnet)[,1],
y=xyC(new_highwayPoints_fishnet)[,2],
colour=factor(ntile(distance_highways,5))),size=1.5) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(new_highwayPoints_fishnet,"distance_highways"),1,8),
name="Quintile\nBreaks") +
geom_sf(data=highways_all, colour = "black") +
geom_sf(data=new_highway, colour="cyan", linewidth=.75) +
geom_sf(data=metro_counties, fill=NA, colour="white", size=.75) +
geom_sf(data = Atlanta_MSA, fill = "transparent",
color = "black", linewidth = .75) +
labs(title = "Distance to Highways",
subtitle = "As fishnet centroids; Existing highways visualized in black; new highway in cyan") +
mapTheme

We then use Model 6 to predict how adding a highway might affect
development patterns in 2029, based scenario 1. While this model does
not show a true supply-side only effect, it is useful because it allows
us to directly compare how adding in a supply-side factor affects the
model’s demand-side only predictions.
dat_highway <-
dat_infill %>%
dplyr::select(-distance_highways) %>%
left_join(as.data.frame(new_highwayPoints_fishnet)) %>%
mutate(predict_2029.distance_highways = predict(Model6,. , type="response"))
grid.arrange(ncol=2,
map_pred29,
dat_highway %>%
ggplot() +
geom_point(aes(x=xyC(dat_highway)[,1], y=xyC(dat_highway)[,2], colour = factor(ntile(predict_2029.distance_highways,5)))) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(dat_highway,"predict_2029.distance_highways"),1,4),
name="% Likelihood\nof Development") +
geom_sf(data=metro_counties, fill=NA, colour="white", size=.75) +
geom_sf(data=highways_all, colour = "black") +
geom_sf(data=new_highway, colour="cyan", linewidth=.75) +
geom_sf(data = Atlanta_MSA, fill = "transparent",
color = "black", linewidth = .75) +
labs(title= "Development Supply in 2029: Predicted Probabilities",
subtitle = "Based on projected development response to a new highway") +
mapTheme
)

While the change is slight, we see that adding in the new highway
does slightly increase the predicted likelihood of development in the
area around it.
Allocation
Now that we better understand the effects of demand-side factors
(like population growth and proximity to developed areas) and
supply-side factors (like new infrastructure available land), we can
analyze these factors by county. This allows us to better understand
which counties in the Atlanta MSA are better suited to development than
others. It also allows us to see which land use and development policies
might be best suited to this region.
Let’s first orient ourselves to how each kind of land cover was
distributed in 2019.
dat2 <-
aggregateRaster(theRasterList19, dat) %>%
dplyr::select(developed19, forest19, farm19, wetlands19, otherUndeveloped19, water19) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat) %>%
st_sf() %>%
st_cast("POLYGON")
rasters19_map <- aggregatedRasters19 %>%
gather(var,value,developed19:water19) %>%
st_cast("POLYGON") %>% #just to make sure no weird geometries slipped in
mutate(X = xyC(.)$x,
Y = xyC(.)$y) %>%
ggplot() +
geom_sf(data=metro_counties) +
geom_point(aes(X,Y, colour=as.factor(value))) +
facet_wrap(~var) +
scale_colour_manual(values = c(gray, highlight),
labels=c("False","True"),
name = "") +
geom_sf(data=metro_counties,
color = "white",
fill = "transparent",
linewidth = .25) +
geom_sf(data=Atlanta_MSA,
color = "black",
fill = "transparent",
linewidth = .5) +
labs(title = "Land Cover Types, 2019",
subtitle = "As fishnet centroids") +
mapTheme +
theme() +
theme(legend.position = "none")
rasters19_map

We see that much of the metro area, including developed areas, is
forested - meaning it has a large amount of tree cover. Farms and other
undeveloped lands tend to be located more on the outer edges. Wetlands
on the other hand are concentrated mostly in the metro region’s southern
counties, along with a number of small ponds and rivers. The largest
water bodies, however, are located in the north of the metro.
Mapping these land cover types separately allows us to see that some
counties may be better suited to development, while others have more
sensitive land cover that should be protected.
However, this method does not allow us to pinpoint where these
locations are with much specificity.
Sensitive Land Cover Lost
To better understand where ecologically important lands are being
lost for development, we create an indicator layer called
sensitive_lost19. This new layer shows where forest or
wetlands were lost to development between 2009 and 2019.
dat2 <-
dat2 %>%
mutate(sensitive_lost19 = ifelse(forest08 == 1 & forest19 == 0 |
wetlands08 == 1 & wetlands19 == 0,1,0)) %>%
st_transform("ESRI:102267")
ggplot() +
geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitive_lost19))) +
scale_colour_manual(values = c(gray, highlight),
labels=c("No Change","Sensitive Lost"),
name = "") +
geom_sf(data = metro_counties, fill = "transparent", color = "white", linewidth = .5) +
geom_sf(data = Atlanta_MSA, fill = "transparent", color = "black", linewidth = .75) +
labs(title = "Sensitive lands lost: 2009 - 2019",
subtitle = "As fishnet centroids") +
mapTheme

Interestingly, we see that more sensitive areas were lost to
development near the metro’s center during this period.
Landscape Fragmentation
To contextualize these sensitive locations, we group areas where the
wetlands19 and forest11 rasters are contiguous
to create a sensitive_regions layer containing all
sensitive regions with an area greater than 1 acre.
sensitiveRegions <-
raster::clump(wetlands19 + forest19) %>% #units are meters
rasterToPolygons() %>%
st_as_sf() %>%
group_by(clumps) %>%
summarize() %>%
mutate(Acres = as.numeric(st_area(.) * 0.00024711)) %>% #There are 0.00024711 acres per square meter and 4046.86 square meters in an acre
filter(Acres > 4046.86) %>%
dplyr::select() %>%
raster::rasterize(.,emptyRaster)
sensitiveRegions[sensitiveRegions > 0] <- 1
names(sensitiveRegions) <- "sensitiveRegions"
dat2 <-
aggregateRaster(c(sensitiveRegions), dat2) %>%
dplyr::select(sensitiveRegions) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat2) %>%
st_sf()
ggplot() +
geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitiveRegions))) +
scale_colour_manual(values = c(gray, highlight),
labels=c("Other","Sensitive Regions"),
name="") +
geom_sf(data = metro_counties, fill = "transparent", color = "white", linewidth = .5) +
geom_sf(data = Atlanta_MSA, fill = "transparent", color = "black", linewidth = .75) +
labs(title = "Sensitive regions",
subtitle = "Continous areas of either wetlands or forests\ngreater than 1 acre") +
mapTheme

This gives us a much more clear understanding of which areas might be
most sensitive to development than mapping each type of land cover
separately did. We see that most of the counties along the outer border
of the Atlanta metro region still have relatively low landscape
fragmentation. This suggests that these counties should prioritize
infill development over suburban sprawl.
Summarize by County
To better understand how these characteristics compare across
counties, we create a matrix of demand side and suitability factors.
This matrix allows us to better understand each county’s suitability
for development.
county_specific_metrics <-
dat2 %>%
#predict development demand from our model
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
#get a count count of grid cells by county which we can use to calculate rates below
left_join(st_set_geometry(dat, NULL) %>% group_by(NAME) %>% summarize(count = n())) %>%
#calculate summary statistics by county
group_by(NAME) %>%
summarize(Total_Farmland = sum(farm19) / max(count),
Total_Forest = sum(forest19) / max(count),
Total_Wetlands = sum(wetlands19) / max(count),
Total_Undeveloped = sum(otherUndeveloped19) / max(count),
Sensitive_Land_Lost = sum(sensitive_lost19) / max(count),
Sensitive_Regions = sum(sensitiveRegions) / max(count),
Mean_Development_Demand = mean(Development_Demand)) %>%
#get population data by county
left_join(countyPopulation_2029 %>%
mutate(Population_Change = county_projection_2029 - county_population_2019,
Population_Change_Rate = Population_Change / county_projection_2029) %>%
dplyr::select(NAME,Population_Change_Rate))
county_specific_metrics %>%
gather(Variable, Value, -NAME, -geometry) %>%
mutate(Variable = factor(Variable, levels=c("Population_Change_Rate","Mean_Development_Demand",
"Total_Farmland","Total_Undeveloped","Total_Forest",
"Total_Wetlands","Sensitive_Land_Lost","Sensitive_Regions",
ordered = TRUE))) %>%
mutate(Planning_Designation = case_when(
Variable == "Population_Change_Rate" | Variable == "Mean_Development_Demand" ~ "Demand-Side",
Variable == "Total_Farmland" | Variable == "Total_Undeveloped" ~ "Suitable",
TRUE ~ "Not Suitable")) %>%
ggplot(aes(x=Variable, y=Value, fill=Planning_Designation)) +
geom_bar(stat="identity", position=position_dodge(), colour="black") +
facet_wrap(~NAME, ncol=5) +
coord_flip() +
scale_y_continuous(breaks = seq(.25, 1, by = .25)) +
geom_vline(xintercept = 2.5) + geom_vline(xintercept = 4.5) +
scale_fill_manual(values=c("#0D0887FF","#BD3786FF","#FDC926FF")) +
labs(title= "County Specific Allocation Metrics", subtitle= "As rates", x="Indicator", y="Rate") +
plotTheme + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="bottom")

This chart shows three types of metrics:
- Demand-side factors
- Factors suitable for development, and
- Factors not suitable for development
The demand side factors are Mean_Development_Demand and
Population_Change_Rate.
Mean_Development_Demand is the mean value of predicted
development demand for a given county in 2029.
Population_Change_Rate is the share of a county’s
population that is projected to move-in between 2019 and 2029.
Factors suitable for development are area of
Total_Farmland and area of Total_Undeveloped
land in a given county.
Factors not suitable for development are area of
Sensitive_Regions, Sensitive_Land_Lost, and
Total_Wetlands in a county.
In determining our allocation criteria, we determined any area with a
wetland was not suitable for development. Attending to the externalities
of urban sprawl, we chose to preserve these vital ecosystems. When it
comes to forests, however, given that the Atlanta MSA has such extensive
tree cover, we determined that forested areas are suitable for
development.
Looking at the development suitability indicators across the 21
counties of Georgia’s MSA, we see that all counties have a large
percentage of forested land and therefore of sensitive regions. Looking
at the rest of the indicators together however, we can see that some
counties are better suited for development than others.
For example, we see that Hall County has both a high rate of
development demand and of population change. It also has a relatively
large amount of farmland and a relatively low amount of total wetlands
and sensitive land lost. Together, these factors suggest that Hall
County has ample space for development that will not impact sensitive
areas (other than forest).
The satellite image below of development in Hall County shows why
there is a large amount of forested land cover across the Atlanta metro
region. There seems to be a regional preference for dispersed
development in forested land.

On the other hand, Clayton County appears to be poorly suited for
development because it has relatively high development demand, is
experiencing population growth, has a low amount of farmland and
undeveloped land, and has a relatively high amount of wetlands.
Let’s look more closely at Hall County (in the North) and Clayton
County (to the South) to see what these traits look like spatially.
ggplot() +
geom_sf(data = metro_counties, fill = gray, color = "white", linewidth = .5) +
geom_sf(data = filter(metro_counties, NAME=="Hall" | NAME=="Clayton"), fill = "#FDC926FF", color = "white", linewidth = .5) +
geom_sf(data = Atlanta_MSA, fill = "transparent", color = "black", linewidth = .75) +
labs(title = "Atlanta MSA",
subtitle = "Hall and Clayton Counties Highlighted") +
mapTheme

Hall County
First, we map supply and demand factors for Hall County, to examine
an example of a county well suited to further development.
HallCounty <-
dat2 %>%
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
filter(NAME == "Hall") %>%
rename("developed19" = developed19...2)
HallCounty_landUse <- rbind(
filter(HallCounty, wetlands19 == 1 ) %>%
dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
filter(HallCounty, developed19 == 1) %>%
dplyr::select() %>% mutate(Land_Use = "Developed"))
grid.arrange(
ggplot() +
geom_sf(data=HallCounty, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
geom_point(data=HallCounty_landUse, aes(x=xyC(HallCounty_landUse)[,1],
y=xyC(HallCounty_landUse)[,2], colour=Land_Use),
shape = 15, size = 3) +
geom_sf(data=st_intersection(AtlantaHighways,filter(metro_counties, NAME=="Hall")), linewidth=1, color = darkGray) +
scale_fill_manual(values = palette5, name="Development\nDemand",
labels=substr(quintileBreaks(HallCounty,"Development_Demand"),1,5)) +
scale_colour_manual(values = c(gray,"#F0F921FF")) +
geom_sf(data = filter(metro_counties, NAME=="Hall"), fill = "transparent", color = "black", linewidth = 1) +
labs(title = "Development Potential, 2029: Hall County",
subtitle = "With Developed and Unsuitable Areas from 2019") +
mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),
ggplot() +
geom_sf(data=HallCounty, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
geom_point(data=HallCounty_landUse, aes(x=xyC(HallCounty_landUse)[,1],
y=xyC(HallCounty_landUse)[,2], colour=Land_Use),
shape = 15, size = 3) +
geom_sf(data=st_intersection(AtlantaHighways, filter(metro_counties, NAME=="Hall")), linewidth=1, color = darkGray) +
scale_fill_manual(values = palette5, name="Population\nChange",
labels=substr(quintileBreaks(HallCounty,"pop_Change"),1,5)) +
scale_colour_manual(values = c(gray,"#F0F921FF")) +
geom_sf(data = filter(metro_counties, NAME=="Hall"), fill = "transparent", color = "black", linewidth = 1) +
labs(title = "Projected Population, 2029: Hall County",
subtitle = "With Developed and Unsuitable Areas from 2019") +
mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

Mapping Hall County’s development potential and projected population
with developed land and wetlands (the “not suitable” land) shows us that
the County still has a significant amount of area that is suitable for
development. In particular, this county would do well to prioritize
infill development between highways in its northwest and southern
portions where there is predicted to be both high development demand and
significant population growth. This would also help to reduce forest
fragmentation and pressure on wetlands in the county’s east and
northeast.
Clayton County
Next, we map the same supply and demand factors for Clayton County to
examine an example of a county not well suited to further
development.
ClaytonCounty <-
dat2 %>%
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
filter(NAME == "Clayton") %>%
rename("developed19" = developed19...2)
ClaytonCounty_landUse <- rbind(
filter(ClaytonCounty, wetlands19 == 1 ) %>%
dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
filter(ClaytonCounty, developed19 == 1) %>%
dplyr::select() %>% mutate(Land_Use = "Developed"))
grid.arrange(
ggplot() +
geom_sf(data=ClaytonCounty, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
geom_point(data=ClaytonCounty_landUse, aes(x=xyC(ClaytonCounty_landUse)[,1],
y=xyC(ClaytonCounty_landUse)[,2], colour=Land_Use),
shape = 15, size = 6) +
geom_sf(data=st_intersection(AtlantaHighways,filter(metro_counties, NAME=="Clayton")), linewidth=1, color = darkGray) +
scale_fill_manual(values = palette5, name="Development\nDemand",
labels=substr(quintileBreaks(ClaytonCounty,"Development_Demand"),1,5)) +
scale_colour_manual(values = c(gray,"#F0F921FF")) +
geom_sf(data = filter(metro_counties, NAME=="Clayton"), fill = "transparent", color = "black", linewidth = 1) +
labs(title = "Development Potential, 2029: Clayton County",
subtitle = "With Developed and Unsuitable Areas from 2019") +
mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),
ggplot() +
geom_sf(data=ClaytonCounty, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
geom_point(data=ClaytonCounty_landUse, aes(x=xyC(ClaytonCounty_landUse)[,1],
y=xyC(ClaytonCounty_landUse)[,2], colour=Land_Use),
shape = 15, size = 6) +
geom_sf(data=st_intersection(AtlantaHighways, filter(metro_counties, NAME=="Clayton")), linewidth=1, color = darkGray) +
scale_fill_manual(values = palette5, name="Population\nChange",
labels=substr(quintileBreaks(ClaytonCounty, "pop_Change"), 1, 5)) +
scale_colour_manual(values = c(gray,"#F0F921FF")) +
geom_sf(data = filter(metro_counties, NAME=="Clayton"), fill = "transparent", color = "black", linewidth = 1) +
labs(title = "Projected Population, 2029: Clayton County",
subtitle = "With Developed and Unsuitable Areas from 2019") +
mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

In contrast to Hall County’s relatively large amount of land that is
suitable for development, we see that Clayton County has almost none.
The county is almost entirely developed and the two areas in the
southern portion of the county that are not developed are dominated by
wetland areas that are not suitable for development.
Furthermore, the map shows that the remaining cells are all near
wetland areas or not expected to see population growth. Therefore,
Clayton County is an ideal location for conservation rather than
development in the Atlanta metro area.
Conclusion
Thank you for walking through our model. What we take away from this
is….
LS0tDQp0aXRsZTogIkxVRU0gQTUgLSBGb3JlY2FzdGluZyBTcHJhd2wiDQphdXRob3I6ICJMaW5kc2V5IEhvdmVyICYgQ2hhcmxpZSBUb3duc2xleSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGhpZ2hsaWdodDogYnJlZXplZGFyaw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkNCnJtKGxpc3Q9bHMoKSkNCg0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpICN0dXJuIG9mZiBzY2llbnRpZmljIG5vdGF0aW9uDQpgYGANCg0KYGBge3IgbGlicmFyaWVzIGFuZCB0aGVtZXN9DQojaW5zdGFsbC5wYWNrYWdlcygiRk5OIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3JlcGVsIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ5YXJkc3RpY2siKQ0KI2luc3RhbGwucGFja2FnZXMoImlncmFwaCIpDQojaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KI3VwZGF0ZS5wYWNrYWdlcygiY2FyZXQiKQ0KI2luc3RhbGwucGFja2FnZXMoInJlYWN0YWJsZSIpDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzZikNCmxpYnJhcnkocmFzdGVyKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkodGlkeWNlbnN1cykNCmxpYnJhcnkodGlncmlzKQ0KbGlicmFyeShGTk4pDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeSh5YXJkc3RpY2spDQpsaWJyYXJ5KHBzY2wpDQpsaWJyYXJ5KHBsb3RST0MpIA0KbGlicmFyeShnZ3JlcGVsKQ0KbGlicmFyeShwUk9DKQ0KbGlicmFyeShncmlkKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkobWFwdG9vbHMpDQpsaWJyYXJ5KHJlYWN0YWJsZSkNCmxpYnJhcnkoY2xhc3NJbnQpDQoNCnBsb3RUaGVtZSA8LSB0aGVtZSgNCiAgcGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksDQogIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksDQogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICMgU2V0IHRoZSBlbnRpcmUgY2hhcnQgcmVnaW9uIHRvIGJsYW5rDQogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICBwbG90LmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICAjcGFuZWwuYm9yZGVyPWVsZW1lbnRfcmVjdChjb2xvdXI9IiNGMEYwRjAiKSwNCiAgIyBGb3JtYXQgdGhlIGdyaWQNCiAgcGFuZWwuZ3JpZC5tYWpvcj1lbGVtZW50X2xpbmUoY29sb3VyPSIjRDBEMEQwIixzaXplPS43NSksDQogIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKQ0KDQptYXBUaGVtZSA8LSB0aGVtZShwbG90LnRpdGxlID1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksDQogICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLA0KICAgICAgICAgICAgICAgICAgYXhpcy5saW5lPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXIgPSAndHJhbnNwYXJlbnQnKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwNCiAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICdjbScpLA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDEsICJjbSIpLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjIsICJjbSIpKQ0KDQojQ29sb3JzOiBWaXJpZGlzIFBsYXNtYSAob3B0aW9uICJDIikNCmdyYXkgPC0gIiNEM0QzRDMiDQpkYXJrR3JheSA8LSAiIzUwNTI1MSINCmhpZ2hsaWdodCA8LSAiIzBEMDg4N0ZGIg0KcGFsZXR0ZTIgPC0gYygiI0ZBOUUzQkZGIiwgIiMwRDA4ODdGRiIpDQpwYWxldHRlMyA8LSBjKCIjRkRDOTI2RkYiLCAiI0Q4NTc2QkZGIiwgIiMwRDA4ODdGRiIpDQpwYWxldHRlNCA8LSBjKCIjRkE5RTNCRkYiLCAiI0Q4NTc2QkZGIiwgIiM5QzE3OUVGRiIsICIjMEQwODg3RkYiKQ0KcGFsZXR0ZTUgPC0gYygiI0ZEQzkyNkZGIiwgIiNFRDc5NTNGRiIsICIjRDg1NzZCRkYiLCAiIzlDMTc5RUZGIiwgIiMwRDA4ODdGRiIpDQpwYWxldHRlMTAgPC0gYygiI0YwRjkyMUZGIiwgIiNGREM5MjZGRiIsICIjRkE5RTNCRkYiLCAiI0VENzk1M0ZGIiwgIiNEODU3NkJGRiIsDQogICAgICAgICAgICAgICAiI0JEMzc4NkZGIiwgIiM5QzE3OUVGRiIsICIjNzMwMUE4RkYiLCAiIzQ3MDM5RkZGIiwgIiMwRDA4ODdGRiIpDQoNCiNSIE1hcmtkb3duIENoZWF0IHNoZWV0IGZvciByZWZlcmVuY2UgLSBodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNS8wMi9ybWFya2Rvd24tY2hlYXRzaGVldC5wZGYNCmBgYA0KDQpgYGB7ciBkaXJlY3RvcmllcywgaW5jbHVkZT1GQUxTRX0NCmRpcl9naXQgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwbyINCg0KZGlyX2N0IDwtICdDOi9Vc2Vycy9jdG93bi9PbmVEcml2ZSAtIFBlbm5PMzY1L0NsYXNzZXMvQ2xhc3Nlc19TZW00XzIwMjNTcHJpbmcvQ1BMTiA2NzVfTGFuZCBNb2RlbGluZy9Bc3NpZ25tZW50cy9BNV9TcHJhd2xGb3JlY2FzdGluZy9MVUVNX0E1X1JlcG8vRGF0YScNCmBgYA0KDQpgYGB7ciBmdW5jdGlvbnN9DQojdGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBhIGNvbHVtbiBpbiB0byBxdWludGlsZXMuIEl0IGlzIHVzZWQgZm9yIG1hcHBpbmcuDQpxdWludGlsZUJyZWFrcyA8LSBmdW5jdGlvbihkZix2YXJpYWJsZSkgew0KICAgIGFzLmNoYXJhY3RlcihxdWFudGlsZShkZltbdmFyaWFibGVdXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYyguMDEsLjIsLjQsLjYsLjgpLG5hLnJtPVQpKQ0KfQ0KDQojVGhpcyBmdW5jdGlvbiBjYW4gYmUgdXNlZCB0byBjb252ZXJ0IGEgcG9seWdvbiBzZiB0byBjZW50cm9pZHMgeHkgY29vcmRzLg0KeHlDIDwtIGZ1bmN0aW9uKGFQb2x5Z29uU0YpIHsNCiAgYXMuZGF0YS5mcmFtZSgNCiAgICBjYmluZCh4PXN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGFQb2x5Z29uU0YpKVssMV0sDQogICAgICAgICAgeT1zdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChhUG9seWdvblNGKSlbLDJdKSkNCn0gDQoNCiN0aGlzIGZ1bmN0aW9uIGNvbnZlcnQgYSByYXN0ZXIgdG8gYSBkYXRhIGZyYW1lIHNvIGl0IGNhbiBiZSBwbG90dGVkIGluIGdncGxvdA0KcmFzdCA8LSBmdW5jdGlvbihpblJhc3Rlcikgew0KICBkYXRhLmZyYW1lKA0KICAgIHh5RnJvbUNlbGwoaW5SYXN0ZXIsIDE6bmNlbGwoaW5SYXN0ZXIpKSwgDQogICAgdmFsdWUgPSBnZXRWYWx1ZXMoaW5SYXN0ZXIpKSB9DQoNCiN0aGlzIGZ1bmN0aW9uIGFnZ3JlZ2F0ZXMgcmFzdGVycw0KYWdncmVnYXRlUmFzdGVyIDwtIGZ1bmN0aW9uKGlucHV0UmFzdGVyTGlzdCwgQXRsYW50YU1TQV9maXNobmV0KSB7DQogICNjcmVhdGUgYW4gZW1wdHkgZmlzaG5ldCB3aXRoIHRoZSBzYW1lIGRpbWVuc2lvbnMgYXMgdGhlIGlucHV0IGZpc2huZXQNCiAgdGhlc2VGaXNobmV0cyA8LSBBdGxhbnRhTVNBX2Zpc2huZXQgJT4lIGRwbHlyOjpzZWxlY3QoKQ0KICAjZm9yIGVhY2ggcmFzdGVyIGluIHRoZSByYXN0ZXIgbGlzdA0KICBmb3IgKGkgaW4gaW5wdXRSYXN0ZXJMaXN0KSB7DQogICNjcmVhdGUgYSB2YXJpYWJsZSBuYW1lIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGl0aCByYXN0ZXINCiAgdmFyTmFtZSA8LSBuYW1lcyhpKQ0KICAjY29udmVydCByYXN0ZXIgdG8gcG9pbnRzIGFzIGFuIHNmDQogICAgdGhlc2VQb2ludHMgPC0NCiAgICAgIHJhc3RlclRvUG9pbnRzKGkpICU+JQ0KICAgICAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICAgICAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhBdGxhbnRhTVNBX2Zpc2huZXQpKSAlPiUNCiAgICAgIGZpbHRlciguW1sxXV0gPT0gMSkNCiAgI2FnZ3JlZ2F0ZSB0byB0aGUgZmlzaG5ldA0KICAgIHRoaXNGaXNobmV0IDwtDQogICAgICBhZ2dyZWdhdGUodGhlc2VQb2ludHMsIEF0bGFudGFNU0FfZmlzaG5ldCwgbGVuZ3RoKSAlPiUNCiAgICAgIG11dGF0ZSghIXZhck5hbWUgOj0gaWZlbHNlKGlzLm5hKC5bWzFdXSksMCwxKSkNCiAgI2FkZCB0byB0aGUgbGFyZ2VyIGZpc2huZXQNCiAgICB0aGVzZUZpc2huZXRzIDwtIGNiaW5kKHRoZXNlRmlzaG5ldHMsIHRoaXNGaXNobmV0KQ0KICB9DQogICNvdXRwdXQgYWxsIGFnZ3JlZ2F0ZXMgYXMgb25lIGxhcmdlIGZpc2huZXQNCiAgIHJldHVybih0aGVzZUZpc2huZXRzKQ0KfQ0KYGBgDQoNCiMgSW50cm9kdWN0aW9uOiBUaGlzIE1vZGVsIGFuZCBJdHMgU2lnbmlmaWNhbmNlIHRvIFBsYW5uZXJzDQoNCldlbGNvbWUhIEluIHRoaXMgaW50ZXJhY3RpdmUgUi1tYXJrZG93biBtZW1vLCB3ZSBlbXBsb3kgaGlzdG9yaWNhbCBwYXR0ZXJucyBvZiBkZXZlbG9wbWVudCBhbmQgcG9wdWxhdGlvbiB0cmVuZHMgaW4gdGhlIEF0bGFudGEsIEdlb3JnaWEgbWV0cm9wb2xpdGFuIHN0YXRpc3RpY2FsIGFyZWEgKCJNU0EiKSB0byBwcmVkaWN0IGZ1dHVyZSBkZXZlbG9wbWVudCBzY2VuYXJpb3MuIFNwZWNpZmljYWxseSwgd2UgZXhhbWluZSB0aGUgc3BhdGlhbCByZWxhdGlvbnNoaXBzIHRoYXQgZXhpc3QgYW1vbmcgcG9wdWxhdGlvbiBncm93dGggYW5kIGhpZ2h3YXkgaW5mcmFzdHJ1Y3R1cmUgd2l0aCBsYW5kIGRldmVsb3BtZW50LiBUaHJvdWdoIHRoZXNlIG1vZGVscywgd2UgZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGVhY2ggb2YgdGhlc2UgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCB1cmJhbiBzcHJhd2wsIG91ciBkZXBlbmRlbnQgdmFyaWFibGUuDQoNClVyYmFuIHNwcmF3bCBpcyBhIGRldmVsb3BtZW50IHNjZW5hcmlvIGNoYXJhY3Rlcml6ZWQgYnkgImxlYXAgZnJvZ2dpbmciIGRldmVsb3BtZW50LCBhIGRldmVsb3BtZW50IHNjZW5hcmlvIHRoYXQgZGlzcHJvcG9ydGlvbmF0ZWx5IGNvbnN1bWVzIGxhbmQgYW5kIGVudmlyb25tZW50YWwgcmVzb3VyY2VzLCB3aXRoIHNpZ25pZmljYW50IG5lZ2F0aXZlIGVjb2xvZ2ljYWwgZXh0ZXJuYWxpdGllcy4gVGhlc2UgbW9kZWxzIHdpbGwgcHJlZGljdCBsaWtlbHkgdXJiYW4gc3ByYXdsIGZvciB0aGUgQXRsYW50YSBtZXRybyBhcmVhIGJhc2VkIG9uIHR3byBzY2VuYXJpb3MuIFRoZSBtb2RlbHMgcHJlc2VudGVkIGhlcmUgZW5hYmxlIHBsYW5uZXJzIGFuZCBwb2xpY3kgbWFrZXJzIHRvIHByZWVtcHRpdmVseSBkZXZlbG9wIHBvbGljaWVzIHRvIGNhbGlicmF0ZSBhZ2FpbnN0IHVyYmFuIHNwcmF3bCwgZW1wb3dlcmluZyBtdW5pY2lwYWwgZ292ZXJubWVudHMgdG8gY3JlYXRlIGNvbnNjaWVudGlvdXMgbGFuZCBhbGxvY2F0aW9uIHBsYW5zLg0KDQpUaGUgbW9kZWwgZGV2ZWxvcGVkIHdpbGwgcHJlZGljdCB0d28gZGlmZmVyZW50IHNjZW5hcmlvczogZGVtYW5kLXNpZGUgYW5kIHN1cHBseS1zaWRlLiBJbiB0aGUgZGVtYW5kLXNpZGUgbW9kZWwsIHdlIGF0dGVtcHQgdG8gdW5kZXJzdGFuZCB0aGUgcmVsYXRpb25zaGlwIHRoYXQgcG9wdWxhdGlvbiBncm93dGggaGFzIHdpdGggdXJiYW4gc3ByYXdsIGluIHRoZSBNU0EuIFNwZWNpZmljYWxseSwgd2UgcHJlZGljdCB0aGUgZGVtYW5kIGZvciBkZXZlbG9wbWVudCBhY2NvcmRpbmcgdG8gcG9wdWxhdGlvbiBncm93dGgsIGFuZCBob3cgdGhpcyB3b3VsZCBzcGF0aWFsbHkgbWFuaWZlc3QgYmFzZWQgb24gZXhpc3RpbmcgbGFuZCB1c2UgdHJlbmRzIGFuZCBwb3B1bGF0aW9uIHByb2plY3Rpb25zLiBUaGUgc3VwcGx5LXNpZGUgbW9kZWwgd2lsbCBwcmVkaWN0IGhvdyBmdXR1cmUgZGV2ZWxvcG1lbnQgbWlnaHQgb2NjdXIgZm9sbG93aW5nIHRoZSBjb25zdHJ1Y3Rpb24gb2YgYSBuZXcsIHRoZW9yZXRpY2FsIGhpZ2h3YXksIHN1cHBseWluZyBpbmZyYXN0cnVjdHVyZS4gVGhyb3VnaCB0aGlzIHNjZW5hcmlvLCB3ZSBhdHRlbXB0IHRvIGFzY2VydGFpbiB0aGUgcmVsYXRpb25zaGlwIHRoYXQgaGlnaHdheSBkZXZlbG9wbWVudCBoYXMgd2l0aCB1cmJhbiBzcHJhd2wuDQoNCkxldCdzIGV4cGxvcmUgaG93IHNwcmF3bCBjYW4gb2NjdXIsIHVubWl0aWdhdGVkLCBiYXNlZCBvbiB0aGVzZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIFdlIHVzZSBsYW5kIGNvdmVyIGFuZCBwb3B1bGF0aW9uIGRhdGEgZnJvbSB0aGUgeWVhcnMgMjAwOC8yMDA5IGFuZCAyMDE5IHRvIHByZWRpY3QgZm9yIHRoZSB5ZWFyIDIwMjkuDQoNCi0gICBSZWdpb24gb2YgaW50ZXJlc3Q6IEF0bGFudGEsIEdBIG1ldHJvDQoNCi0gICBZZWFycyBvZiBzdHVkeTogMjAwOC0yMDE5DQoNCi0gICBQcm9qZWN0aW5nIHRvIDIwMjkNCg0KLSAgIERhdGEgbGV2ZWw6IGZpc2huZXQgZ3JpZCBjZWxsDQoNClRoZSBmb2xsb3dpbmcgY2h1bmtzIG9mIGNvZGUgd2lsbCBjb2xsZWN0IHRoZSBwcmVyZXF1aXNpdGUgZGF0YSBuZWVkZWQgZm9yIHRoZSBtb2RlbCBmcm9tIHRoZSB0d28geWVhcnMgb2Ygc3R1ZHksIHN0cnVjdHVyZSB0aGUgZGF0YSBpbiBwcmVwYXJhdGlvbiBmb3IgbW9kZWxpbmcsIGFuZCBydW4gdGhlIGRlbWFuZCAtIGFuZCBzdXBwbHktIHNpZGUgbW9kZWxzIGJhc2VkIG9uIHRoZSBzdHVkeSB5ZWFycycgZGF0YSB0byBwcm9qZWN0IGZvciB0aGUgeWVhciAyMDMwLg0KDQojIERhdGEgR2F0aGVyaW5nIGFuZCBGZWF0dXJlIEVuZ2luZWVyaW5nDQoNCkZpcnN0LCB3ZSBwdWxsIGluIGFuZCB2aXN1YWxpemUgb3VyIHNwYXRpYWwgZGF0YXNldHMgb2YgdGhlIEF0bGFudGEgTVNBJ3MgMjEgY291bnRpZXMuDQoNCmBgYHtyIGNyZWF0aW5nIEdlb0pTT04gZnJvbSBTaGFwZWZpbGUsIGluY2x1ZGU9RkFMU0UsIGV2YWw9RkFMU0V9DQojU0hQMSA8LSBzdF9yZWFkKCJDOi9Vc2Vycy9MaW5kc2V5L0Rlc2t0b3AvQ1BMTjY3NV9MYW5kX1VzZV9Nb2RlbGluZ19EZXNrdG9wL0ZpbmFsL0xVRU1fQTVfUmVwby9TaGFwZWZpbGVzL1NIUDEuc2hwIikNCg0KI0dlb0pTT04xIDwtIHN0X3dyaXRlKFNIUDEsICJDOi9Vc2Vycy9MaW5kc2V5L0Rlc2t0b3AvQ1BMTjY3NV9MYW5kX1VzZV9Nb2RlbGluZ19EZXNrdG9wL0ZpbmFsL0xVRU1fQTVfUmVwby9HZW9KU09Ocy9TSFAxLmdlb2pzb24iKQ0KDQojR2VvSlNPTl94IDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby9kOTg1OWZjMTA2OWRkODlkNDRmY2FmMzQ1ZTcyMmE5YmY2MDFhNTUwL0RhdGEvZ2VvanNvbi9HZW9KU09OX3guZ2VvanNvbiIpICMjIyBzcGVjaWZ5IHRoZSBmaWxlIG5hbWUgKG5vdGU6IHRoaXMgaXMgdGhlIHBlcm1hbGluaywgYnV0IHlvdSBjaGFuZ2UgdGhlIGRvbWFpbiB0byByYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tIEFORCB5b3UgcmVtb3ZlIC9ibG9iLykNCg0KDQojQXRsYW50YV9tZXRyb19ib3VuZGF5IDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby9kOTg1OWZjMTA2OWRkODlkNDRmY2FmMzQ1ZTcyMmE5YmY2MDFhNTUwL0RhdGEvZ2VvanNvbi9BdGxhbnRhX01ldHJvX0JvdW5kYXJ5Lmdlb2pzb24iKQ0KDQpgYGANCg0KQW5kIHJldHJpZXZlIHRoZSBBdGxhbnRhIE1TQSBib3VuZGFyeSB1c2luZyB0aGUgYHRpZ3Jpc2AgcGFja2FnZS4NCg0KYGBge3IgQXRsYW50YSBNU0Egc2hhcGVmaWxlLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiNMaXN0IG9mIHRoZSBBdGxhbnRhIHJlZ2lvbiBNUE9zIDIxIGNvdW50aWVzDQpDb3VudGllcyA8LSBjKCJCYXJyb3ciLCAiQmFydG93IiwgIkNhcnJvbGwiLCAiQ2hlcm9rZWUiLCANCiAgICAgICAgICAgICAgIkNsYXl0b24iLCAiQ29iYiIsICJDb3dldGEiLCAiRGF3c29uIiwgDQogICAgICAgICAgICAgICJEZUthbGIiLCAiRG91Z2xhcyIsICJGYXlldHRlIiwgIkZvcnN5dGgiLCANCiAgICAgICAgICAgICAgIkZ1bHRvbiIsICJHd2lubmV0dCIsICJIYWxsIiwgIkhlbnJ5IiwgDQogICAgICAgICAgICAgICJOZXd0b24iLCAiUGF1bGRpbmciLCAiUm9ja2RhbGUiLCAiU3BhbGRpbmciLCAiV2FsdG9uIikNCg0KI0ZpcnN0IHB1bGwgYWxsIE1TQXMgaW4gdGhlIFUuUy4sIHRoZW4gc2VhcmNoIGZvciB5b3VycyBhbmQgZmlsdGVyIHdpdGggdGhlIGNvcnJlY3QgQ0JTQUZQIGNvZGUNCm1ldHJvX2NvdW50aWVzIDwtIGNvdW50aWVzKHN0YXRlID0gIkdBIiwgY2IgPSBUUlVFLCByZXNvbHV0aW9uID0gIjUwMGsiLCB5ZWFyID0gTlVMTCkgJT4lDQogIGZpbHRlcihOQU1FICVpbiUgQ291bnRpZXMpICU+JSANCiAgc3RfdHJhbnNmb3JtKCJFU1JJOjEwMjI2NyIpDQoNCiNNZXJnZSBjb3VudGllcyBpbnRvIG9uZSBzaGFwZWZpbGUgc2hvd2luZyBqdXN0IHRoZSBib3VuZGFyeQ0KQXRsYW50YV9NU0EgPC0gc3RfdW5pb24obWV0cm9fY291bnRpZXMpICU+JSANCiAgc3RfdHJhbnNmb3JtKCJFU1JJOjEwMjI2NyIpDQpgYGANCg0KYGBge3IgcGxvdCBtc2F9DQojcGxvdCBNU0EgYm91bmRhcnkgdG8gbWFrZSBzdXJlIGl0cyByaWdodA0KZ2dwbG90KCkgKyANCiAgZ2VvbV9zZihkYXRhID0gbWV0cm9fY291bnRpZXMpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBtYXBUaGVtZSArIA0KICBsYWJzKHRpdGxlID0gIkF0bGFudGEgMjEgQ291bnR5IE1TQSIpDQpgYGANCg0KDQpgYGB7ciBtc2EgY29udmVyc2lvbiwgaW5jbHVkZT1GQUxTRSwgZXZhbD1GQUxTRX0NCiMjIFNLSVAgIyMNCiNDb252ZXJ0IHNoYXBlZmlsZSB0byBnZW9qc29uIHNvIGNhbiBzdG9yZSBvbiBnaXRodWINCiNzdF93cml0ZShtZXRyb19jb3VudGllcywgYXBwZW5kID0gRkFMU0UsICJDOi9Vc2Vycy9jdG93bi9PbmVEcml2ZSAtIFBlbm5PMzY1L0NsYXNzZXMvQ2xhc3Nlc19TZW00XzIwMjNTcHJpbmcvQ1BMTiA2NzVfTGFuZCBNb2RlbGluZy9Bc3NpZ25tZW50cy9BNV9TcHJhd2xGb3JlY2FzdGluZy9MVUVNX0E1X1JlcG8vRGF0YS9nZW9qc29uL0F0bGFudGFfTWV0cm9fQ291bnRpZXMuZ2VvanNvbiIpDQoNCiNzdF93cml0ZShtZXRyb19jb3VudGllcywgYXBwZW5kID0gRkFMU0UsICJDOi9Vc2Vycy9jdG93bi9PbmVEcml2ZSAtIFBlbm5PMzY1L0NsYXNzZXMvQ2xhc3Nlc19TZW00XzIwMjNTcHJpbmcvQ1BMTiA2NzVfTGFuZCBNb2RlbGluZy9Bc3NpZ25tZW50cy9BNV9TcHJhd2xGb3JlY2FzdGluZy9MVUVNX0E1X1JlcG8vRGF0YS9zaGFwZWZpbGUvQXRsYW50YV9NZXRyb19Cb3VuZGFyeS9BdGxhbnRhX01ldHJvX0NvdW50aWVzLnNocCIpDQoNCiNzdF93cml0ZShtZXRyb19jb3VudGllcywgYXBwZW5kID0gRkFMU0UsICJDOi9Vc2Vycy9jdG93bi9PbmVEcml2ZSAtIFBlbm5PMzY1L0NsYXNzZXMvQ2xhc3Nlc19TZW00XzIwMjNTcHJpbmcvQ1BMTiA2NzVfTGFuZCBNb2RlbGluZy9Bc3NpZ25tZW50cy9BNV9TcHJhd2xGb3JlY2FzdGluZy9MVUVNX0E1X1JlcG8vRGF0YS9nZW9qc29uL0F0bGFudGFNU0FfQm91bmRhcnlfMjFjb3VudGllcy5nZW9qc29uIikNCg0KI3N0X3dyaXRlKG1ldHJvX2NvdW50aWVzLCBhcHBlbmQgPSBGQUxTRSwgIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0xVRU1fQTVfUmVwby9EYXRhL3NoYXBlZmlsZS9BdGxhbnRhX01ldHJvX0JvdW5kYXJ5XzIxY291bnR5L0F0bGFudGFNU0FfQm91bmRhcnlfMjFjb3VudGllcy5zaHAiKQ0KYGBgDQoNCldlIHRoZW4gY3JlYXRlIGEgZmlzaG5ldCBvZiB0aGUgQXRsYW50YSBNU0EgdXNpbmcgMTAwMCcgeCAxMDAwJyBjZWxscy4gQSBmaXNobmV0IGlzIGEgdmVjdG9yIGRhdGEgc2V0IHRoYXQgd2lsbCBhbGxvdyB1cyB0byBhdHRhY2ggb3RoZXIgaW5mb3JtYXRpb24gdG8gdGhlIGZvcnRoY29taW5nIHJhc3RlciBkYXRhIHNldCBvbiBsYW5kIGNvdmVyLg0KDQpgYGB7ciBmaXNobmV0fQ0KQXRsYW50YU1TQV9maXNobmV0IDwtIA0KICAgIHN0X21ha2VfZ3JpZChtZXRyb19jb3VudGllcywgY2VsbHNpemUgPSAxMDAwLCBzcXVhcmUgPSBUUlVFKSAlPiUNCiAgc3Rfc2YoKSAlPiUgDQogIHN0X3RyYW5zZm9ybSgiRVNSSToxMDIyNjciKQ0KDQpBdGxhbnRhTVNBX2Zpc2huZXQgPC0NCiAgQXRsYW50YU1TQV9maXNobmV0W21ldHJvX2NvdW50aWVzLF0NCg0KI3Bsb3QgTVNBIGJvdW5kYXJ5IHRvIG1ha2Ugc3VyZSBpdHMgcmlnaHQNCmdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGEgPSBBdGxhbnRhTVNBX2Zpc2huZXQsDQogICAgICAgICAgZmlsbCA9ICJsaWdodGdyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCANCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJBdGxhbnRhIE1TQSBGaXNobmV0IikgKw0KICBtYXBUaGVtZQ0KYGBgDQoNCiMjIExhbmQgQ292ZXINCg0KIyMjIExhbmQgQ292ZXIgYnkgWWVhciBhbmQgVHlwZQ0KDQpXZSByZXRyaWV2ZSB0aGUgcmFzdGVyIGZpbGVzIG9mIGxhbmQgY292ZXIgZm9yIHRoZSBBdGxhbnRhIE1TQS4gV2UgZG93bmxvYWRlZCB0aGVzZSBkYXRhc2V0cyBmcm9tIHRoZSBNdWx0aS1SZXNvbHV0aW9uIExhbmQgQ2hhcmFjdGVyaXN0aWNzIENvbnNvcnRpdW0ncyBOYXRpb25hbCBMYW5kIENvdmVyIERhdGFiYXNlLCBhbmQgY2xpcHBlZCB0aGVtIGluIEFyY0dJUyB0byBhIHJlY3Rhbmd1bGFyIGJvdW5kYXJ5IGV4dGVudCB0aGF0IGFsaWduZWQgd2l0aCB0aGUgZmFydGhlc3QgcG9pbnRzIG9mIHRoZSBBdGxhbnRhIHJlZ2lvbidzIDIxIGNvdW50eSBib3JkZXIuDQoNCmBgYHtyIEltcG9ydCBmdWxsIG9yaWdpbmFsIExhbmQgQ292ZXIgZGF0YSwgZXZhbD1GQUxTRX0NCiMjU0tJUCMjDQojbG9hZCBmdWxsIHNpemUgcmFzdGVycw0KbGNfMjAwOCA9IHJhc3RlcigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2MtdG93bnNsZXkvTFVFTV9BNV9SZXBvLzU2YmQzNWIzNDNkMzllODhiZDhlMWZkYzg5OTU3ZTZjY2I4MDRmZTkvRGF0YS9yYXN0ZXIvMjAwOExDX3Byb2oudGlmIikNCiAgICANCmxjXzIwMTkgPSByYXN0ZXIoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby81NmJkMzViMzQzZDM5ZTg4YmQ4ZTFmZGM4OTk1N2U2Y2NiODA0ZmU5L0RhdGEvcmFzdGVyLzIwMTlMQ19wcm9qLnRpZiIpDQpgYGANCg0KVGhlIHJhc3RlciBmaWxlcyB3ZXJlIHN0aWxsIHZlcnkgbGFyZ2UsIHNvIHdlIGRvd24tc2FtcGxlZCB0aGVtIHRvIG1ha2UgdGhlaXIgc2l6ZSBtb3JlIG1hbmFnZWFibGUgYW5kIHRoZW4gY2xpcHBlZCB0aGVtIHByZWNpc2VseSB0byB0aGUgTVNBIGJvdW5kYXJ5Lg0KDQpgYGB7ciBkb3duc2FtcGxlIHJhc3RlcnMgY3JvcCBhbmQgZXhwb3J0LCBldmFsPUZBTFNFfQ0KIyMgU0tJUCAjIw0KDQojZG93biBzYW1wbGUgdGhlIHJhc3RlciB0byBtYWtlIG1vcmUgbWFuYWdlYWJsZQ0KbGNfMjAwOCA8LSBhZ2dyZWdhdGUobGNfMjAwOCwgZmFjdCA9IDcsIGZ1biA9IG1vZGFsKQ0KbGNfMjAxOSA8LSBhZ2dyZWdhdGUobGNfMjAxOSwgZmFjdCA9IDcsIGZ1biA9IG1vZGFsKQ0KDQojY3JvcCByYXN0ZXJzDQpsY18yMDA4X2Nyb3AgPC0gY3JvcChsY18yMDA4LCBleHRlbnQobWV0cm9fY291bnRpZXMpKQ0KbGNfMjAxOV9jcm9wIDwtIGNyb3AobGNfMjAxOSwgZXh0ZW50KG1ldHJvX2NvdW50aWVzKSkgDQoNCiNtYXNrIHJhc3RlcnMNCmxjXzIwMDhfY3JvcCA8LSBtYXNrKHggPSBsY18yMDA4X2Nyb3AsIG1hc2sgPSBtZXRyb19jb3VudGllcykNCmxjXzIwMTlfY3JvcCA8LSBtYXNrKHggPSBsY18yMDE5X2Nyb3AsIG1hc2sgPSBtZXRyb19jb3VudGllcykNCg0KI0V4cG9ydCBkb3duc2l6ZWQgcmFzdGVycw0KI3dyaXRlUmFzdGVyKGxjXzIwMDhfY3JvcCwgZmlsZW5hbWU9ZmlsZS5wYXRoKHJhc3RfZGlyLCAibGMyMDA4X3NtbC50aWYiKSwgZm9ybWF0PSJHVGlmZiIsIG92ZXJ3cml0ZT1UUlVFKQ0KDQojd3JpdGVSYXN0ZXIobGNfMjAxOV9jcm9wLCBmaWxlbmFtZT1maWxlLnBhdGgoIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0xVRU1fQTVfUmVwby9EYXRhL3Jhc3RlciIsICJsYzIwMTlfc21sLnRpZiIpLCBmb3JtYXQ9IkdUaWZmIiwgb3ZlcndyaXRlPVRSVUUpDQpgYGANCg0KYGBge3IgaW1wb3J0IGRvd25zYW1wbGVkIHJhc3RlcnN9DQpsY18yMDA4IDwtIHJhc3RlcigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2MtdG93bnNsZXkvTFVFTV9BNV9SZXBvLzI0ZjA4ZTYxYzVlYzk4M2IzZmEzOWYxZjNiMDNmYjkwYWEzYTEzZWUvRGF0YS9yYXN0ZXIvbGMyMDA4X3NtbC50aWYiKSANCg0KbGNfMjAxOSA8LSByYXN0ZXIoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby8yNGYwOGU2MWM1ZWM5ODNiM2ZhMzlmMWYzYjAzZmI5MGFhM2ExM2VlL0RhdGEvcmFzdGVyL2xjMjAxOV9zbWwudGlmIikgDQpgYGANCg0KVGhpcyBhbGxvd3MgdXMgdG8gdmlzdWFsaXplIEF0bGFudGEncyBsYW5kIGNvdmVyIGluIDIwMDggYW5kIDIwMTkuDQoNCmBgYHtyIFBsb3QgbGFuZCBjb3ZlciBmb3IgMjAwOCBhbmQgMjAxOSwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9DQpncmlkLmFycmFuZ2UobmNvbCA9IDEsDQpnZ3Bsb3QoKSArDQogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChsY18yMDA4KSAlPiUgbmEub21pdCAlPiUgZmlsdGVyKHZhbHVlID4gMCksIA0KICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBuYW1lID0iIiwNCiAgICAgICAgICAgICAgICAgICAgIG9wdGlvbiA9ICJDIikgKw0KICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMsDQogICAgICAgICAgY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC41KSArDQogICAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyLCAyMDA4IikgKw0KICBtYXBUaGVtZSArDQogIHRoZW1lKGxlZ2VuZC5kaXJlY3Rpb249Imhvcml6b250YWwiKSwNCg0KDQpnZ3Bsb3QoKSArDQogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChsY18yMDE5KSAlPiUgbmEub21pdCAlPiUgZmlsdGVyKHZhbHVlID4gMCksIA0KICAgICAgICAgICAgICBhZXMoeCwgeSwgZmlsbD1hcy5mYWN0b3IodmFsdWUpKSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSIiLA0KICAgICAgICAgICAgICAgICAgICAgb3B0aW9uID0gIkMiKSArDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjUpICsNCiAgICAgIGdlb21fc2YoZGF0YT1BdGxhbnRhX01TQSwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciwgMjAxOSIpICsNCiAgbWFwVGhlbWUgKw0KICB0aGVtZShsZWdlbmQuZGlyZWN0aW9uPSJob3Jpem9udGFsIikNCikNCmBgYA0KDQpWaWV3aW5nIGxhbmQgY292ZXIgYXQgdGhlIE1TQSBsZXZlbCBpbiAyMDA4IGFuZCAyMDE5IHNob3dzIHRoYXQgY2hhbmdlIGlzIGhhcHBlbmluZyBhdCB0b28gc21hbGwgYSBzY2FsZSB0byBkaXNjZXJuIHZpc3VhbGx5IGF0IHRoaXMgZXh0ZW50LiBUaGVyZWZvcmUsIHdlIHN1bW1vbiB0aGUgcG93ZXJzIG9mIFIuIEZpcnN0LCB3ZSBkaXN0aWxsIHRoZSBtYW55IHNwZWNpZmljIGxhbmQgY292ZXIgY2xhc3NpZmljYXRpb25zIGludG8gc2l4IHNpbXBsZSBidWNrZXRzOiBkZXZlbG9wZWQgbGFuZCwgZm9yZXN0LCBmYXJtbGFuZCwgd2V0bGFuZHMsIHdhdGVyIGJvZGllcywgYW5kIG90aGVyIHVuZGV2ZWxvcGVkIGxhbmRzLg0KDQojIyMgTGFuZCBDb3ZlciBieSBUeXBlDQoNCmBgYHtyIHJlY2xhc3MgTGFuZCBDb3Zlcn0NCmRldmVsb3BlZDA4IDwtIGxjXzIwMDggPT0gMjEgfCBsY18yMDA4ID09IDIyIHwgbGNfMjAwOCA9PSAyMyB8IGxjXzIwMDggPT0gMjQNCmZvcmVzdDA4IDwtIGxjXzIwMDggPT0gNDEgfCBsY18yMDA4ID09IDQyIHwgbGNfMjAwOCA9PSA0MyANCmZhcm0wOCA8LSBsY18yMDA4ID09IDgxIHwgbGNfMjAwOCA9PSA4MiANCndldGxhbmRzMDggPC0gbGNfMjAwOCA9PSA5MCB8IGxjXzIwMDggPT0gOTUgDQpvdGhlclVuZGV2ZWxvcGVkMDggPC0gbGNfMjAwOCA9PSA1MiB8IGxjXzIwMDggPT0gNzEgfCBsY18yMDA4ID09IDMxIA0Kd2F0ZXIwOCA8LSBsY18yMDA4ID09IDExDQoNCmRldmVsb3BlZDE5IDwtIGxjXzIwMTkgPT0gMjEgfCBsY18yMDE5ID09IDIyIHwgbGNfMjAxOSA9PSAyMyB8IGxjXzIwMTkgPT0gMjQNCmZvcmVzdDE5IDwtIGxjXzIwMTkgPT0gNDEgfCBsY18yMDE5ID09IDQyIHwgbGNfMjAxOSA9PSA0MyANCmZhcm0xOSA8LSBsY18yMDE5ID09IDgxIHwgbGNfMjAxOSA9PSA4MiANCndldGxhbmRzMTkgPC0gbGNfMjAxOSA9PSA5MCB8IGxjXzIwMTkgPT0gOTUgDQpvdGhlclVuZGV2ZWxvcGVkMTkgPC0gbGNfMjAxOSA9PSA1MiB8IGxjXzIwMTkgPT0gNzEgfCBsY18yMDE5ID09IDMxIA0Kd2F0ZXIxOSA8LSBsY18yMDE5ID09IDExDQoNCm5hbWVzKGRldmVsb3BlZDA4KSA8LSAiZGV2ZWxvcGVkMDgiDQpuYW1lcyhmb3Jlc3QwOCkgPC0gImZvcmVzdDA4Ig0KbmFtZXMoZmFybTA4KSA8LSAiZmFybTA4Ig0KbmFtZXMod2V0bGFuZHMwOCkgPC0gIndldGxhbmRzMDgiDQpuYW1lcyhvdGhlclVuZGV2ZWxvcGVkMDgpIDwtICJvdGhlclVuZGV2ZWxvcGVkMDgiDQpuYW1lcyh3YXRlcjA4KSA8LSAid2F0ZXIwOCINCg0KbmFtZXMoZGV2ZWxvcGVkMTkpIDwtICJkZXZlbG9wZWQxOSINCm5hbWVzKGZvcmVzdDE5KSA8LSAiZm9yZXN0MTkiDQpuYW1lcyhmYXJtMTkpIDwtICJmYXJtMTkiDQpuYW1lcyh3ZXRsYW5kczE5KSA8LSAid2V0bGFuZHMxOSINCm5hbWVzKG90aGVyVW5kZXZlbG9wZWQxOSkgPC0gIm90aGVyVW5kZXZlbG9wZWQxOSINCm5hbWVzKHdhdGVyMTkpIDwtICJ3YXRlcjE5Ig0KYGBgDQoNClRoaXMgZGlzdGlsbGVkLCBjYXRlZ29yaWNhbCBkYXRhc2V0IGZvciBlYWNoIHllYXIgaXMgdGhlbiBhZ2dyZWdhdGVkIHRvIG91ciBmaXNobmV0IG9mIHRoZSBBdGxhbnRhIE1TQS4gVGhlIHJlc3VsdGluZyBtYXBzIHNob3cgdGhhdCBBdGxhbnRhIGlzIGEgaGVhdmlseSBmb3Jlc3RlZCBhcmVhLiBEZXZlbG9wbWVudCBpcyB2YXN0LCB3aXRoIGl0cyBhbmNob3IgaW4gdGhlIGNlbnRlciBvZiB0aGUgTVNBLCByYWRpYXRpbmcgb3V0d2FyZC4gQSBzaWduaWZpY2FudCBhbW91bnQgb2YgZmFybWxhbmQgZXhpc3RzIGluIGEgdGhpY2sgYmFuZCBhZ2FpbnN0IHRoZSBNU0EncyBwZXJpcGhlcnkuIFVuZGV2ZWxvcGVkIGFyZWFzIGFyZSBzY2F0dGVyZWQsIGFsc28gbW9zdGx5IG5lYXIgdGhlIE1TQSdzIHBlcmlwaGVyeS4NCg0KYGBge3IgdHVybiByYXN0ZXJzIGludG8gZmlzaG5ldCBkYXRhZnJhbWUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCg0KdGhlUmFzdGVyTGlzdDA4IDwtIGMoZGV2ZWxvcGVkMDgsIGZvcmVzdDA4LCBmYXJtMDgsIHdldGxhbmRzMDgsIG90aGVyVW5kZXZlbG9wZWQwOCwgd2F0ZXIwOCkNCg0KdGhlUmFzdGVyTGlzdDE5IDwtIGMoZGV2ZWxvcGVkMTksIGZvcmVzdDE5LCBmYXJtMTksIHdldGxhbmRzMTksIG90aGVyVW5kZXZlbG9wZWQxOSwgd2F0ZXIxOSkNCg0KYWdncmVnYXRlZFJhc3RlcnMwOCA8LQ0KICBhZ2dyZWdhdGVSYXN0ZXIodGhlUmFzdGVyTGlzdDA4LCBBdGxhbnRhTVNBX2Zpc2huZXQpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRldmVsb3BlZDA4LCBmb3Jlc3QwOCwgZmFybTA4LCB3ZXRsYW5kczA4LCBvdGhlclVuZGV2ZWxvcGVkMDgsIHdhdGVyMDgpICU+JQ0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgYXMuZmFjdG9yKQ0KDQphZ2dyZWdhdGVkUmFzdGVyczE5IDwtDQogIGFnZ3JlZ2F0ZVJhc3Rlcih0aGVSYXN0ZXJMaXN0MTksIEF0bGFudGFNU0FfZmlzaG5ldCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcGVkMTksIGZvcmVzdDE5LCBmYXJtMTksIHdldGxhbmRzMTksIG90aGVyVW5kZXZlbG9wZWQxOSwgd2F0ZXIxOSkgJT4lDQogIG11dGF0ZV9pZihpcy5udW1lcmljLCBhcy5mYWN0b3IpDQoNCnJhc3RlcnMwOF9tYXAgPC0gYWdncmVnYXRlZFJhc3RlcnMwOCAlPiUNCiAgZ2F0aGVyKHZhcix2YWx1ZSxkZXZlbG9wZWQwODp3YXRlcjA4KSAlPiUNCiAgc3RfY2FzdCgiUE9MWUdPTiIpICU+JSAgICAjanVzdCB0byBtYWtlIHN1cmUgbm8gd2VpcmQgZ2VvbWV0cmllcyBzbGlwcGVkIGluDQogIG11dGF0ZShYID0geHlDKC4pJHgsDQogICAgICAgICBZID0geHlDKC4pJHkpICU+JQ0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzKSArDQogICAgZ2VvbV9wb2ludChhZXMoWCxZLCBjb2xvdXI9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAgICBmYWNldF93cmFwKH52YXIpICsNCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgaGlnaGxpZ2h0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJGYWxzZSIsIlRydWUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKw0KICAgIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjI1KSArDQogICAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNSkgKw0KICAgIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBUeXBlcywgMjAwOCIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgIG1hcFRoZW1lICsNCiAgIHRoZW1lKCkgICsNCiAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KcmFzdGVyczA4X21hcA0KYGBgDQoNCiMjIyBMYW5kIENvdmVyIENoYW5nZQ0KDQpUbyBpZGVudGlmeSBhcmVhcyB3aXRoIGxhbmQgY292ZXIgY2hhbmdlLCB3ZSB1c2UgdGhlIHJhc3RlciBkYXRhIHNldHMgYW5kIHJlY2xhc3NpZnkgdGhlIGxhbmQgY292ZXIgdHlwZXMgYXMgbnVtZXJpY2FsIHZhbHVlcywgd2hlcmUgZGV2ZWxvcGVkIGxhbmRzIGhhdmUgdGhlIHZhbHVlIG9mIDEgYW5kIHVuZGV2ZWxvcGVkIGxhbmRzIGhhdmUgdGhlIHZhbHVlIG9mIDAsIGZvciBlYWNoIG9mIHRoZSB5ZWFycycgZGF0YSBzZXRzLiBXZSBhcmUgdGhlbiBhYmxlIHRvIGFkZCB0aGVzZSBkYXRhIHNldHMgdG9nZXRoZXIgb24gYSBjZWxsLWJ5LWNlbGwgYmFzaXMgdG8gdW5kZXJzdGFuZCB3aGVyZSBkZXZlbG9wbWVudCBoYXMgb2NjdXJyZWQgaW4gMjAxOSBhbmQgd2hlcmUgaXQgd2FzIG5vdCBpbiAyMDA4LiBXaGVuIGFkZGVkIHRvZ2V0aGVyLCB0aGUgY2VsbHMgd2l0aCB2YWx1ZXMgb2YgMSByZXByZXNlbnQgd2hlcmUgbmV3IGRldmVsb3BtZW50IGhhcyBvY2N1cnJlZDsgYSB2YWx1ZSBvZiAyIHNob3dzIHdoZXJlIGRldmVsb3BtZW50IHdhcyBpbiAyMDA4IGFuZCByZW1haW5lZCBpbiAyMDE5LCBhbmQgYSB2YWx1ZSBvZiAwIHNob3dzIGxhbmQgdGhhdCB3YXMgYW5kIHJlbWFpbnMgdW5kZXZlbG9wZWQuIFRoaXMgZnVuY3Rpb24gaXMgbGlrZSB1c2luZyByYXN0ZXIgY2FsY3VsYXRvciBpbiBHSVMuDQoNCmBgYHtyIHJlY2xhc3MgbWF0cml4LCByZXN1bHRzPSdoaWRlJ30NCiNjcmVhdCBtYXRyaXggZm9yIHJlY2xhc3NpZnlpbmcgbGFuZCBjb3Zlcg0KcmVjbGFzc01hdHJpeCA8LSANCiAgbWF0cml4KGMoDQogICAgMCwxMiwwLA0KICAgIDEyLDI0LDEsDQogICAgMjQsSW5mLDApLA0KICBuY29sPTMsIGJ5cm93PVQpDQoNCnJlY2xhc3NNYXRyaXgNCmBgYA0KDQpgYGB7ciBkZXZlbG9wZWQgbGFuZCBjb3ZlciBjaGFuZ2UgcGxvdH0NCiNyZWNsYXNzaWZ5IGxhbmQgY292ZXIgY2hhbmdlIHVzaW5nIHJlY2xhc3MgbWF0cml4DQoNCmxjXzIwMDhyIDwtIA0KICByZWNsYXNzaWZ5KGxjXzIwMDgsIHJlY2xhc3NNYXRyaXgpDQpsY18yMDE5ciA8LSANCiAgcmVjbGFzc2lmeShsY18yMDE5LCByZWNsYXNzTWF0cml4KQ0KDQpsY18yMDA4cltsY18yMDA4ciA8IDFdIDwtIDANCm5hbWVzKGxjXzIwMDhyKSA8LSAiZGV2MDgiDQoNCmxjXzIwMTlyW2xjXzIwMTlyIDwgMV0gPC0gMA0KbmFtZXMobGNfMjAxOXIpIDwtICJkZXYxOSINCg0KZGV2ZWxvcG1lbnRfY2hhbmdlIDwtIGxjXzIwMDhyICsgbGNfMjAxOXINCg0KIyBjb252ZXJ0IHRoZSByYXN0ZXIgdG8gYSB2ZWN0b3INCmRldl9jaGFuZ2VfdmVjIDwtIGFzLnZlY3RvcihkZXZlbG9wbWVudF9jaGFuZ2UpDQoNCiMgY3JlYXRlIHRoZSBoaXN0b2dyYW0gcGxvdCB3aXRoIGdncGxvdDINCmdncGxvdChkYXRhLmZyYW1lKHZhbHVlID0gZGV2X2NoYW5nZV92ZWMpLCBhZXMoeD12YWx1ZSkpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPS41LCBmaWxsPWhpZ2hsaWdodCwgY29sb3I9IndoaXRlIikgKw0KICBsYWJzKHRpdGxlPSJEZXZlbG9wbWVudCBDaGFuZ2UgYnkgVHlwZSAoMjAwOC0yMDE5KSIsDQogICAgICAgc3VidGl0bGUgPSAiMCA9IFJlbWFpbmVkIFVuZGV2ZWxvcGVkLCAxID0gTmV3IERldmVsb3BtZW50LCAyID0gQWxyZWFkeSBEZXZlbG9wZWQiLA0KICAgICAgIHg9IkRldmVsb3BtZW50IENoYW5nZSIsDQogICAgICAgeT0iRnJlcXVlbmN5IikgKw0KICBwbG90VGhlbWUNCmBgYA0KDQpUaGlzIHNob3dzIHVzIHRoYXQgbW9zdCBjZWxscyBpbiB0aGUgTVNBIHJlbWFpbmVkIHVuZGV2ZWxvcGVkIGZyb20gMjAwOCB0byAyMDE5LCBtYW55IG9mIHRoZW0gd2VyZSBhbHJlYWR5IGRldmVsb3BlZCwgYW5kIGEgZmV3IG9mIHRoZSBwcmV2aW91c2x5IHVuZGV2ZWxvcGVkIGNlbGxzIGNvbnZlcnRlZCB0byBkZXZlbG9wZWQuIFdlIGNhbiB0aGVuIHZpc3VhbGl6ZSB3aGVyZSB0aGUgbmV3IGRldmVsb3BtZW50IGNlbGxzIHdpdGggdGhlIDEgdmFsdWUgYXJlLCB3aGljaCBzaG93cyB0aGF0IG5ldyBkZXZlbG9wbWVudCBoYXMgYmVlbiBzY2F0dGVyZWQgdGhyb3VnaG91dCB0aGUgTVNBLg0KDQpgYGB7ciBwbG90dGluZyBsYW5kIGNvdmVyIGNoYW5nZSwgZmlnLndpZHRoPTh9DQoNCmRldmVsb3BtZW50X2NoYW5nZVtkZXZlbG9wbWVudF9jaGFuZ2UgIT0gMV0gPC0gTkENCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9QXRsYW50YV9NU0EpICsNCiAgZ2VvbV9yYXN0ZXIoZGF0YT1yYXN0KGRldmVsb3BtZW50X2NoYW5nZSkgJT4lIG5hLm9taXQsIA0KICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBvcHRpb24gPSAiQyIsDQogICAgICAgICAgICAgICAgICAgICBuYW1lID0iQ2VsbHMgXG53aXRoIExhbmQgQ292ZXJcbkNoYW5nZSIpICsgDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjUpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgTGFuZCBDb3ZlciBDaGFuZ2UgKDIwMDgtMjAxOSkiLA0KICAgICAgIHN1YnRpdGxlID0gIkF0bGFudGEgbWV0cm8gYXJlYSB8IFJhc3RlciBjZWxscyIpICsNCiAgbWFwVGhlbWUgKw0KICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KICANCmBgYA0KDQpXZSB0aGVuIGpvaW4gdGhpcyBkYXRhc2V0LCBtYXBwaW5nIG5ldyBkZXZlbG9wbWVudCwgdG8gb3VyIGZpc2huZXQuIFRoaXMgaGFzIHRoZSBlZmZlY3Qgb2YgbWFnbmlmeWluZyB3aGVyZSB0aGUgZGV2ZWxvcG1lbnQgY2hhbmdlIGhhcyBoYXBwZW5lZCBhIGJpdC4NCg0KYGBge3IgbGFuZCBjb3ZlciBkZXZlbG9wbWVudCBjaGFuZ2UsIGZpZy53aWR0aD04fQ0KZGV2X2NoYW5nZSA8LSByZWNsYXNzaWZ5KGRldmVsb3BtZW50X2NoYW5nZSwgcmVjbGFzc01hdHJpeCkNCiAgICAgICAgICAgICAgICAgICAgICAgICANCmRldl9jaGFuZ2VbZGV2ZWxvcG1lbnRfY2hhbmdlIDwgMV0gPC0gTkENCg0KbmFtZXMoZGV2X2NoYW5nZSkgPC0gImRldmVsb3BtZW50X2NoYW5nZSINCg0KDQpjaGFuZ2VQb2ludHMgPC0NCiAgcmFzdGVyVG9Qb2ludHMoZGV2X2NoYW5nZSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhBdGxhbnRhTVNBX2Zpc2huZXQpKQ0KDQpkZXZfY2hhbmdlX2Zpc2huZXQgPC0gDQogIGFnZ3JlZ2F0ZShjaGFuZ2VQb2ludHMsIEF0bGFudGFNU0FfZmlzaG5ldCwgc3VtKSAlPiUNCiAgbXV0YXRlKGRldmVsb3BtZW50X2NoYW5nZSA9IGlmZWxzZShpcy5uYShkZXZlbG9wbWVudF9jaGFuZ2UpLDAsMSksDQogICAgICAgICBkZXZlbG9wbWVudF9jaGFuZ2UgPSBhcy5mYWN0b3IoZGV2ZWxvcG1lbnRfY2hhbmdlKSkNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGRhdGE9ZGV2X2NoYW5nZV9maXNobmV0LCANCiAgICAgICAgICAgICBhZXMoeD14eUMoZGV2X2NoYW5nZV9maXNobmV0KSR4LCB5PXh5QyhkZXZfY2hhbmdlX2Zpc2huZXQpJHksIGNvbG91cj1kZXZlbG9wbWVudF9jaGFuZ2UpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhncmF5LCBoaWdobGlnaHQpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMsDQogICAgICAgICAgY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC41KSArDQogICAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgKDIwMTkpIiwgc3VidGl0bGUgPSAiQXRsYW50YSBtZXRybyBhcmVhIHwgRmlzaG5ldCBjZW50cm9pZHMiKSArDQogIG1hcFRoZW1lDQoNCiNkZXZfY2hhbmdlX2Zpc2huZXQgPC0gc3Rfd3JpdGUoZGV2X2NoYW5nZV9maXNobmV0LCAiQzovVXNlcnMvTGluZHNleS9EZXNrdG9wL0NQTE42NzVfTGFuZF9Vc2VfTW9kZWxpbmdfRGVza3RvcC9GaW5hbC9MVUVNX0E1X1JlcG8vRGF0YS9nZW9qc29uL2Rldl9jaGFuZ2VfZmlzaG5ldC5nZW9qc29uIikNCg0KYGBgDQoNCiMjIENlbnN1cyBEYXRhOiBQb3B1bGF0aW9uDQoNCk5leHQsIHdlIHNldCB1cCBvdXIgTVNBIHBvcHVsYXRpb24gZGF0YSwgd2hpY2ggd2lsbCBwcmVwYXJlIHVzIGZvciB0aGUgZGVtYW5kLXNpZGUgbW9kZWwuIFRoZSBuZXh0IHR3byBjaHVua3MgZG93bmxvYWQgdGhlIHBvcHVsYXRpb24gZGF0YSBmb3IgMjAwOSBhbmQgMjAxOSBmcm9tIHRoZSBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IDUteWVhciBFc3RpbWF0ZXMgb24gYSBjZW5zdXMgdHJhY3QgbGV2ZWwuDQoNCmBgYHtyIG1ldHJvIHBvcCAyMDA5LCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiMgTG9vayBoZXJlIGZvciB2YXJpYWJsZSBjb2Rlcw0KYWNzX3ZhcnMgPC0gbG9hZF92YXJpYWJsZXMoMjAwOSwgImFjczUiKQ0KDQojUHVsbCAyMDA5IHBvcHVsYXRpb24NClBvcDA5IDwtIA0KICBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsIHZhcmlhYmxlcyA9ICJCMDEwMDNfMDAxIiwgeWVhciA9IDIwMDksDQogICAgICAgICAgICAgICAgc3RhdGUgPSAxMywgZ2VvbWV0cnkgPSBUUlVFLCANCiAgICAgICAgICAgICAgICBjb3VudHkgPSBDb3VudGllcykgJT4lDQogIHJlbmFtZSgicG9wMDkiID0gZXN0aW1hdGUpICU+JSANCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhBdGxhbnRhTVNBX2Zpc2huZXQpKQ0KYGBgDQoNCmBgYHtyIG1ldHJvIHBvcCAyMDE5LCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiNQdWxsIDIwMTkgcG9wdWxhdGlvbg0KUG9wMTkgPC0gDQogIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgdmFyaWFibGVzID0gIkIwMTAwM18wMDEiLCB5ZWFyID0gMjAxOSwNCiAgICAgICAgICAgICAgICBzdGF0ZSA9IDEzLCBnZW9tZXRyeSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgIGNvdW50eSA9IENvdW50aWVzKSAlPiUNCiAgcmVuYW1lKCJwb3AxOSIgPSBlc3RpbWF0ZSkgJT4lIA0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKEF0bGFudGFNU0FfZmlzaG5ldCkpICU+JSANCiAgc3RfYnVmZmVyKC0xKSAjYnVmZmVyIHRoZSB0aGUgdHJhY3RzIGJ5IC0xZnQuIFRoaXMgaXMgZG9uZSBiZWNhdXNlIHRpZHljZW5zdXMgYXBwZWFycyB0byByZXR1cm4gZ2VvbWV0cmllcyB0aGF0IGFyZSBwcm9ibGVtYXRpYyB3aGVuIHN1YmplY3RlZCB0byB0aGUgYXJlYSB3ZWlnaHRlZCBpbnRlcnBvbGF0aW9uIGZ1bmN0aW9uDQpgYGANCg0KV2UgdGhlbiB2aXN1YWxpemUgdGhlIHBvcHVsYXRpb24gZm9yIHRoZSB0d28geWVhcnMgYnkgdGhlIHJlc3BlY3RpdmUgY2Vuc3VzIHRyYWN0IGJvdW5kYXJpZXMgZm9yIGVhY2ggb2YgdGhlIHllYXJzLg0KDQpgYGB7ciBwbG90IGJvdGggcG9wIHllYXJzLCBmaWcud2lkdGg9MTJ9DQpncmlkLmFycmFuZ2UoDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IFBvcDA5LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wMDksNSkpKSwgY29sb3VyPU5BKSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCANCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1xdWludGlsZUJyZWFrcyhQb3AwOSwicG9wMDkiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKw0KICBsYWJzKHRpdGxlPSIyMDA5IFBvcHVsYXRpb25cbkF0bGFudGEgMjEtQ291bnR5IE1TQSIsDQogICAgICAgc3VidGl0bGU9IkJ5IENlbnN1cyBUcmFjdCIpICsNCiAgbWFwVGhlbWUsDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gUG9wMTksIGFlcyhmaWxsPWZhY3RvcihudGlsZShwb3AxOSw1KSkpLCBjb2xvdXI9TkEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1xdWludGlsZUJyZWFrcyhQb3AxOSwicG9wMTkiKSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlPSIyMDE5IFBvcHVsYXRpb25cbkF0bGFudGEgMjEtQ291bnR5IE1TQSIsDQogICAgICAgc3VidGl0bGU9IkJ5IENlbnN1cyBUcmFjdCIpICsNCiAgbWFwVGhlbWUsIG5jb2w9MikNCmBgYA0KDQpXZSB0aGVuIHJlY29uY2lsZSB0aGUgdHJhY3QgYm91bmRhcmllcyB3aXRoIHRoZSBmaXNobmV0IGdyaWQgY2VsbHMgdG8gYmUgYWJsZSB0byBjb25kdWN0IGFuYWx5c2lzIGFuZCBidWlsZCBtb2RlbHMgd2l0aCBvdXIgZmlzaG5ldCBkYXRhIHNldC4NCg0KYGBge3IgcmVjb25jaWxlIHRyYWN0cyBhbmQgZmlzaG5ldCBjZWxsc30NCiMjIyBCcmVha2luZyBoZXJlIG9uIHNlbGVjdChmaXNobmV0SUQpDQpBdGxhbnRhTVNBX2Zpc2huZXQgPC0NCiAgQXRsYW50YU1TQV9maXNobmV0ICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oImZpc2huZXRJRCIpICU+JSANCiAgbXV0YXRlKGZpc2huZXRJRCA9IGFzLm51bWVyaWMoZmlzaG5ldElEKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoZmlzaG5ldElEKQ0KDQpmaXNobmV0UG9wMDkgPC0NCiAgc3RfaW50ZXJwb2xhdGVfYXcoUG9wMDlbInBvcDA5Il0sIEF0bGFudGFNU0FfZmlzaG5ldCwgZXh0ZW5zaXZlPVRSVUUpICU+JQ0KICBhcy5kYXRhLmZyYW1lKC4pICU+JQ0KICByb3duYW1lc190b19jb2x1bW4odmFyID0gImZpc2huZXRJRCIpICU+JQ0KICBsZWZ0X2pvaW4oQXRsYW50YU1TQV9maXNobmV0ICU+JQ0KICAgICAgICAgICAgICBtdXRhdGUoZmlzaG5ldElEID0gYXMuY2hhcmFjdGVyKGZpc2huZXRJRCkpLA0KICAgICAgICAgICAgLiwgYnk9YygiZmlzaG5ldElEIj0nZmlzaG5ldElEJykpICU+JSANCiAgbXV0YXRlKHBvcDA5ID0gcmVwbGFjZV9uYShwb3AwOSwwKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QocG9wMDkpDQoNCmZpc2huZXRQb3AxOSA8LQ0KICBzdF9pbnRlcnBvbGF0ZV9hdyhQb3AxOVsicG9wMTkiXSwgQXRsYW50YU1TQV9maXNobmV0LCBleHRlbnNpdmU9VFJVRSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoLikgJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiZmlzaG5ldElEIikgJT4lDQogIGxlZnRfam9pbihBdGxhbnRhTVNBX2Zpc2huZXQgJT4lDQogICAgICAgICAgICAgIG11dGF0ZShmaXNobmV0SUQgPSBhcy5jaGFyYWN0ZXIoZmlzaG5ldElEKSksDQogICAgICAgICAgICAuLCBieT1jKCJmaXNobmV0SUQiPSdmaXNobmV0SUQnKSkgJT4lIA0KICBtdXRhdGUocG9wMTkgPSByZXBsYWNlX25hKHBvcDE5LDApKSAlPiUNCiAgZHBseXI6OnNlbGVjdChwb3AxOSkNCg0KZmlzaG5ldFBvcCA8LSANCiAgY2JpbmQoZmlzaG5ldFBvcDA5LCBmaXNobmV0UG9wMTkpICU+JQ0KICBkcGx5cjo6c2VsZWN0KHBvcDA5LHBvcDE5KSAlPiUNCiAgbXV0YXRlKHBvcF9DaGFuZ2UgPSBwb3AxOSAtIHBvcDA5KQ0KYGBgDQoNCldlIGNhbiB0aGVuIGNvbXBhcmUgcG9wdWxhdGlvbiBieSB0cmFjdCB2cyBwb3B1bGF0aW9uIGJ5IGdyaWQgY2VsbC4NCg0KYGBge3IgcGxvdCBwb3AgdHJhY3QgYW5kIGdyaWQgY2VsbCwgZmlnLndpZHRoPTEyfQ0KZ3JpZC5hcnJhbmdlKA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9UG9wMTksIGFlcyhmaWxsPWZhY3RvcihudGlsZShwb3AxOSw1KSkpLGNvbG91cj1OQSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhQb3AxOSwicG9wMTkiKSwxLDQpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCANCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZT0iMjAxOSBQb3B1bGF0aW9uXG5BdGxhbnRhIDIxLUNvdW50eSBNU0EiLA0KICAgICAgIHN1YnRpdGxlPSJSZXByZXNlbnRlZCBhcyB0cmFjdHM7IEJvdW5kYXJpZXMgb21pdHRlZCIpICsNCiAgbWFwVGhlbWUsDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPWZpc2huZXRQb3AsIGFlcyhmaWxsPWZhY3RvcihudGlsZShwb3AxOSw1KSkpLCBjb2xvdXI9TkEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhmaXNobmV0UG9wLCJwb3AxOSIpLDEsNCksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlPSIyMDE5IFBvcHVsYXRpb25cbkF0bGFudGEgMjEtQ291bnR5IE1TQSIsDQogICAgICAgc3VidGl0bGU9IlJlcHJlc2VudGVkIGFzIGZpc2huZXQgZ3JpZGNlbGxzOyBCb3VuZGFyaWVzIG9taXR0ZWQiKSArDQogIG1hcFRoZW1lLCBuY29sPTIpDQpgYGANCg0KIyMgSGlnaHdheSBEaXN0YW5jZQ0KDQpOZXh0LCB3ZSBwcmVwYXJlIG91ciBzdXBwbHktc2lkZSBtb2RlbCBieSBsb2FkaW5nIGluIHNwYXRpYWwgZGF0YSBmb3IgQXRsYW50YSdzIGV4aXN0aW5nIGhpZ2h3YXlzLg0KDQpgYGB7ciBoaWdod2F5IHRvIGdlb2pzb24sIGluY2x1ZGU9RkFMU0UsIGV2YWw9RkFMU0V9DQojU2tpcA0KI21ldHJvX2hpZ2h3YXlzIDwtIHN0X3JlYWQoIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0xVRU1fQTVfUmVwby9EYXRhL3NoYXBlZmlsZS9BdGxhbnRhTWV0cm9fTWFqb3JfUm9hZHMvTWFqb3JfUm9hZHMuc2hwIikNCg0KI3N0X3dyaXRlKG1ldHJvX2hpZ2h3YXlzLCBhcHBlbmQgPSBGQUxTRSwgIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0xVRU1fQTVfUmVwby9EYXRhL2dlb2pzb24vTWFqb3JfUm9hZHMuZ2VvanNvbiIpDQpgYGANCg0KYGBge3IgaGlnaHdheXMsIGZpZy53aWR0aD04LCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KQXRsYW50YUhpZ2h3YXlzIDwtDQogIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby84YjY4OTY1MTI4NmJiNGZmMjg1NTQwOTViZDM5Y2UxYmQ4NzY1NWU0L0RhdGEvZ2VvanNvbi9NYWpvcl9Sb2Fkcy5nZW9qc29uIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMobWV0cm9fY291bnRpZXMpKSAlPiUNCiAgc3RfaW50ZXJzZWN0aW9uKG1ldHJvX2NvdW50aWVzKSAlPiUgDQogIGZpbHRlcihGRUFUVVJFX1RZID09ICJTdGF0ZSBIaWdod2F5IikNCg0KZW1wdHlSYXN0ZXIgPC0gZGV2ZWxvcG1lbnRfY2hhbmdlDQplbXB0eVJhc3RlcltdIDwtIE5BDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPWRldl9jaGFuZ2VfZmlzaG5ldCwgDQogICAgICAgICAgICAgYWVzKHg9eHlDKGRldl9jaGFuZ2VfZmlzaG5ldCkkeCwgeT14eUMoZGV2X2NoYW5nZV9maXNobmV0KSR5LCBjb2xvdXI9ZGV2ZWxvcG1lbnRfY2hhbmdlKSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgIiNFRDc5NTNGRiIpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFIaWdod2F5cywgY29sb3IgPSBoaWdobGlnaHQsIGxpbmV3aWR0aCA9IC41KSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlID0gIkhpZ2h3YXlzIGFuZCBOZXcgRGV2ZWxvcG1lbnQiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KVGhpcyBzdWdnZXN0cyB0aGVyZSBtYXkgYmUgYW4gYXNzb2NpYXRpb24gYmV0d2VlbiBwcm94aW1pdHkgdG8gaGlnaHdheXMgYW5kIG5ldyBkZXZlbG9wbWVudC4gVG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhpcyByZWxhdGlvbnNoaXAsIHdlIGNhbGN1bGF0ZSBob3cgZmFyIGVhY2ggZmlzaG5ldCBjZWxsIGlzIGZyb20gYSBoaWdod2F5LiBXZSBkbyB0aGlzIGJ5IGNvbnZlcnRpbmcgdGhlIGhpZ2h3YXkgbGF5ZXIgdG8gcmFzdGVyLCBjb252ZXJ0aW5nIHRoZSByYXN0ZXIgdG8gcG9pbnRzLCBhbmQgdGhlbiBjYWxjdWxhdGluZyB0aGUgbWVhbiBkaXN0YW5jZSBmcm9tIGEgaGlnaHdheSBmb3IgZWFjaCBncmlkIGNlbGwuDQoNCmBgYHtyIGNvbnZlcnQgaGlnaHdheXMgdG8gcmFzdGVyLCBldmFsPUZBTFNFfQ0KI1NLSVAjDQojY3JlYXRlIGEgcmFzdGVyIGxheWVyIG9mIGhpZ2h3YXlzDQpoaWdod2F5X3Jhc3RlciA8LSANCiAgYXMoQXRsYW50YUhpZ2h3YXlzLCdTcGF0aWFsJykgJT4lDQogIHJhc3Rlcml6ZSguLGVtcHR5UmFzdGVyKQ0KDQojRXhwb3J0IHJhc3RlcnMNCg0KcmFzdF9kaXIgPSBwYXN0ZShkaXJfY3QsICdyYXN0ZXInLCBzZXA9Jy8nKQ0KDQojd3JpdGVSYXN0ZXIoaGlnaHdheV9yYXN0ZXIsIGZpbGVuYW1lPWZpbGUucGF0aChyYXN0X2RpciwgImhpZ2h3YXlfcmFzdGVyLnRpZiIpLCBmb3JtYXQ9IkdUaWZmIiwgb3ZlcndyaXRlPVRSVUUpDQpgYGANCg0KVGhpcyB2aXN1YWxpemVzIGVhY2ggY2VsbCdzIGRpc3RhbmNlIGZyb20gYSBoaWdod2F5Lg0KDQpgYGB7ciBkaXN0IGZyb20gaGlnaHdheXMsIGVjaG89RkFMU0UsIGZpZy53aWR0aD04fQ0KI2ltcG9ydCBoaWdod2F5IHJhc3Rlcg0KaGlnaHdheV9yYXN0ZXIgPC0gcmFzdGVyKHBhc3RlKGRpcl9naXQsICJhNWE1YjBjOTA5OWI5N2Y2OGMwZDFhZWFkY2RmZTlkYmQyOTYwYTlhL0RhdGEvcmFzdGVyL2hpZ2h3YXlfcmFzdGVyLnRpZiIsIHNlcD0nLycpKQ0KDQpoaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSA8LSBkaXN0YW5jZShoaWdod2F5X3Jhc3RlcikNCm5hbWVzKGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSA8LSAiZGlzdGFuY2VfaGlnaHdheXMiDQoNCiNFeHBvcnQgaGlnaHdheSByYXN0ZXIgZGlzdGFuY2UgdGhhbiBpbXBvcnQgZmlsZSBmcm9tIGdpdGh1YiB0byBzcGVlZCBwcm9jZXNzaW5nDQojaGlnaHdheV9yYXN0ZXIgPC0gcmFzdGVyKHBhc3RlKGRpcl9naXQsICJhNWE1YjBjOTA5OWI5N2Y2OGMwZDFhZWFkY2RmZTlkYmQyOTYwYTlhL0RhdGEvcmFzdGVyL2hpZ2h3YXlfcmFzdGVyLnRpZiIsIHNlcD0nLycpKQ0KDQpoaWdod2F5UG9pbnRzIDwtDQogIHJhc3RlclRvUG9pbnRzKGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKEF0bGFudGFNU0FfZmlzaG5ldCkpDQoNCmhpZ2h3YXlQb2ludHNfZmlzaG5ldCA8LSANCiAgYWdncmVnYXRlKGhpZ2h3YXlQb2ludHMsIEF0bGFudGFNU0FfZmlzaG5ldCwgbWVhbikgJT4lDQogIG11dGF0ZShkaXN0YW5jZV9oaWdod2F5cyA9IGlmZWxzZShpcy5uYShkaXN0YW5jZV9oaWdod2F5cyksMCxkaXN0YW5jZV9oaWdod2F5cykpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzKSArDQogIGdlb21fcG9pbnQoZGF0YT1oaWdod2F5UG9pbnRzX2Zpc2huZXQsIGFlcyh4PXh5QyhoaWdod2F5UG9pbnRzX2Zpc2huZXQpWywxXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhoaWdod2F5UG9pbnRzX2Zpc2huZXQpWywyXSwgDQogICAgICAgICAgICAgICAgIGNvbG91cj1mYWN0b3IobnRpbGUoZGlzdGFuY2VfaGlnaHdheXMsNSkpKSxzaXplPTEuNSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoaGlnaHdheVBvaW50c19maXNobmV0LCJkaXN0YW5jZV9oaWdod2F5cyIpLDEsOCksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFIaWdod2F5cywgY29sb3VyID0gImJsYWNrIikgKw0KICBnZW9tX3NmKGRhdGE9QXRsYW50YV9NU0EsDQogICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIHRvIEhpZ2h3YXlzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJJbiBGZWV0OyBIaWdod2F5cyB2aXN1YWxpemVkIGluIGJsYWNrIikgKw0KICBtYXBUaGVtZQ0KYGBgDQoNCiMjIENhbGN1bGF0aW5nIHRoZSBTcGF0aWFsIExhZyBvZiBEZXZlbG9wbWVudA0KDQpXZSBtZWFzdXJlIGFjY2Vzc2liaWxpdHkgYnkgd2F5IG9mIGEgc3BhdGlhbCBsYWcsIGh5cG90aGVzaXppbmcgdGhhdCBuZXcgZGV2ZWxvcG1lbnQgaXMgYSBmdW5jdGlvbiBvZiBkaXN0YW5jZSB0byBleGlzdGluZyBkZXZlbG9wbWVudC4gVGhlIHNob3J0ZXIgdGhlIGRpc3RhbmNlLCB0aGUgbW9yZSBhY2Nlc3NpYmxlIGEgZ3JpZCBjZWxsIGlzIHRvIGV4aXN0aW5nIGRldmVsb3BtZW50LiBXZSBtZWFzdXJlIHRoaXMgYnkgY2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgY2VsbCB0byBpdHMgMiBuZWFyZXN0IGRldmVsb3BlZCBuZWlnaGJvcmluZyBncmlkIGNlbGxzIGluIDIwMDkuDQoNCmBgYHtyIG5uIGZ1bmN0aW9ufQ0Kbm5fZnVuY3Rpb24gPC0gZnVuY3Rpb24obWVhc3VyZUZyb20sbWVhc3VyZVRvLGspIHsNCiAgI2NvbnZlcnQgdGhlIHNmIGxheWVycyB0byBtYXRyaWNlcw0KICBtZWFzdXJlRnJvbV9NYXRyaXggPC0NCiAgICBhcy5tYXRyaXgobWVhc3VyZUZyb20pDQogIG1lYXN1cmVUb19NYXRyaXggPC0NCiAgICBhcy5tYXRyaXgobWVhc3VyZVRvKQ0KICBubiA8LSAgIA0KICAgIGdldC5rbm54KG1lYXN1cmVUbywgbWVhc3VyZUZyb20sIGspJG5uLmRpc3QNCiAgICBvdXRwdXQgPC0NCiAgICBhcy5kYXRhLmZyYW1lKG5uKSAlPiUNCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gInRoaXNQb2ludCIpICU+JQ0KICAgIGdhdGhlcihwb2ludHMsIHBvaW50X2Rpc3RhbmNlLCBWMTpuY29sKC4pKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lDQogICAgZ3JvdXBfYnkodGhpc1BvaW50KSAlPiUNCiAgICBzdW1tYXJpemUocG9pbnREaXN0YW5jZSA9IG1lYW4ocG9pbnRfZGlzdGFuY2UpKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lIA0KICAgIGRwbHlyOjpzZWxlY3QoLXRoaXNQb2ludCkgJT4lDQogICAgcHVsbCgpDQogIA0KICByZXR1cm4ob3V0cHV0KSAgDQp9DQpgYGANCg0KYGBge3IgZGV2ZWxvcG1lbnQgc3BhdGlhbCBsYWcsIGZpZy53aWR0aD04fQ0KQXRsYW50YU1TQV9maXNobmV0JGxhZ0RldmVsb3BtZW50MDggPC0NCiAgICBubl9mdW5jdGlvbih4eUMoQXRsYW50YU1TQV9maXNobmV0KSwNCiAgICAgICAgICAgICAgICB4eUMoZmlsdGVyKGFnZ3JlZ2F0ZWRSYXN0ZXJzMDgsIGRldmVsb3BlZDA4PT0xKSksDQogICAgICAgICAgICAgICAgMikNCg0KIyBjb21wdXRlIG5hdHVyYWwgYnJlYWtzIGZvciAibGFnRGV2ZWxvcG1lbnQwOCINCmJyZWFrcyA8LSBjbGFzc0ludGVydmFscyhBdGxhbnRhTVNBX2Zpc2huZXQkbGFnRGV2ZWxvcG1lbnQwOCwgbj00LCBzdHlsZT0iamVua3MiKQ0KDQojUGxvdA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMpICsNCiAgZ2VvbV9wb2ludChkYXRhPUF0bGFudGFNU0FfZmlzaG5ldCwgDQogICAgICAgICAgICAgYWVzKHg9eHlDKEF0bGFudGFNU0FfZmlzaG5ldClbLDFdLCB5PXh5QyhBdGxhbnRhTVNBX2Zpc2huZXQpWywyXSwgDQogICAgICAgICAgICAgICAgIGNvbG91cj1jdXQobGFnRGV2ZWxvcG1lbnQwOCwgYnJlYWtzJGJya3MpKSwgc2l6ZT0xLjUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9cGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWZvcm1hdChicmVha3MkYnJrcywgbnNtYWxsPTEpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IkRpc3RhbmNlIChmdCkiKSArDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjUpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJTcGF0aWFsIExhZyB0byAyMDA4IERldmVsb3BtZW50IiwNCiAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgbWFwVGhlbWUNCmBgYA0KDQpUaGlzIHNob3dzIG1vc3Qgb2YgdGhlIGFyZWEgaW4gdGhlIE1TQSdzIGlubmVyIGNvdW50aWVzIHdhcyBuZWFyIGV4aXN0aW5nIGRldmVsb3BtZW50IGluIDIwMDguIEl0IHdhcyBvbmx5IHRoZSBjb3VudGllcyBhdCB0aGUgb3V0ZXIgZnJpbmdlcyB0aGF0IHN0aWxsIGhhZCBhIGZldyBhcmVhcyByZWxhdGl2ZWx5IGRpc3RhbnQgZnJvbSBkZXZlbG9wbWVudC4NCg0KIyMgQ3JlYXRlIEZpbmFsIERhdGFzZXQNCg0KV2UgdGhlbiBhZ2dyZWdhdGUgYWxsIHRoZXNlIGRhdGEgdG8gYSBmaW5hbCBmaXNobmV0IGRhdGEgc2V0IGZvciBmZWVkaW5nIGludG8gb3VyIG1vZGVsLiBUaGlzIGZpc2huZXQgaW5jbHVkZXMgY29sdW1ucyB3aGljaCBpbmRpY2F0ZSwgY2VsbC1ieS1jZWxsLCBpZiB0aGVyZSBoYXMgYmVlbiBkZXZlbG9wbWVudCBjaGFuZ2UsIGxhbmQgdXNlIHR5cGUsIHRoZSBjZWxsJ3MgZGlzdGFuY2UgdG8gaGlnaHdheXMsIHRoZSBjZWxsJ3MgZGlzdGFuY2UgdG8gZXhpc3RpbmcgZGV2ZWxvcG1lbnQsIGFuZCBpdHMgcG9wdWxhdGlvbiBpbiBib3RoIHllYXJzLg0KDQpgYGB7ciBjb21iaW5lIGFsbCB2YXJpYWJsZXMgaW50byBvbmUgZGF0YXNldH0NCmRhdCA8LSAgDQogIGNiaW5kKA0KICAgIEF0bGFudGFNU0FfZmlzaG5ldCwgZGV2X2NoYW5nZV9maXNobmV0LCBoaWdod2F5UG9pbnRzX2Zpc2huZXQsIGZpc2huZXRQb3AsIGFnZ3JlZ2F0ZWRSYXN0ZXJzMDgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRldmVsb3BtZW50X2NoYW5nZSwgZGV2ZWxvcGVkMDgsIGZvcmVzdDA4LCBmYXJtMDgsIHdldGxhbmRzMDgsIG90aGVyVW5kZXZlbG9wZWQwOCwgd2F0ZXIwOCwNCiAgICAgICAgICAgICAgICBwb3AwOSwgcG9wMTksIHBvcF9DaGFuZ2UsIGRpc3RhbmNlX2hpZ2h3YXlzLCBsYWdEZXZlbG9wbWVudDA4KSAlPiUNCiAgc3Rfam9pbihtZXRyb19jb3VudGllcykgJT4lDQogIG11dGF0ZShkZXZlbG9wZWQxOSA9IGlmZWxzZShkZXZlbG9wbWVudF9jaGFuZ2UgID09IDEgJiBkZXZlbG9wZWQwOCA9PSAxLCAwLCAxKSkgJT4lICNjaGFybGllIHRvIGNvbmZpcm0gaWYgdGhlIGVsc2UgdmFsdWUgc2hvdWxkIGJlIDEgb3IgJ2RldmVsb3BlZCcNCiAgZmlsdGVyKHdhdGVyMDggPT0gMCkgDQpgYGANCg0KIyBEYXRhIEV4cGxvcmF0aW9uDQoNCk5vdyB0aGF0IGFsbCBvZiBvdXIgZGF0YSBhcmUgYXNzZW1ibGVkLCB3ZSBleHBsb3JlIGhvdyBlYWNoIGZhY3RvciB1bmNvdmVyZWQgdGh1cyBmYXIgaXMgcmVsYXRlZCB0byBuZXcgZGV2ZWxvcG1lbnQuIFRoaXMgcGxvdCBkaXNwbGF5cyBkaXN0YW5jZSB0byBoaWdod2F5cyBhbmQgdGhlIHNwYXRpYWwgbGFnIG9mIGRldmVsb3BtZW50IGFzIGNvbnRpbnVvdXMgdmFyaWFibGVzLiBUaGlzIHNob3dzIHVzIHRoYXQgbmV3IGRldmVsb3BtZW50IGhhcyBiZWVuLCBvbiBhdmVyYWdlLCBjbG9zZXIgdG8gaGlnaHdheXMsIGFuZCBjbG9zZXIgdG8gZXhpc3RpbmcgZGV2ZWxvcG1lbnQgaW4gYWdncmVnYXRlLCB0aGFuIGNlbGxzIHRoYXQgaGFkIG5vIGNoYW5nZSBpbiBsYW5kIGNvdmVyLiBUaGlzIHN1cHBvcnRzIG91ciBwcmV2aW91cyBoeXBvdGhlc2lzIHRoYXQgbmV3IGRldmVsb3BtZW50IG1pZ2h0IGJlIG9jY3VycmluZyBjbG9zZXIgdG8gaGlnaHdheXMgYW5kIHRvIHByZXZpb3VzbHkgZGV2ZWxvcGVkIGFyZWFzLg0KDQpgYGB7ciBwbG90dGluZyB0aGUgYXZlcmFnZXMgKG1lYW5zKSBvZiBpbmZyYXN0cnVjdHVyZS9kZXZlbG9wbWVudH0NCg0KZGF0ICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRpc3RhbmNlX2hpZ2h3YXlzLGxhZ0RldmVsb3BtZW50MDgsZGV2ZWxvcG1lbnRfY2hhbmdlKSAlPiUgDQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1kZXZlbG9wbWVudF9jaGFuZ2UsIC1nZW9tZXRyeSkgJT4lDQogIGdncGxvdCguLCBhZXMoZGV2ZWxvcG1lbnRfY2hhbmdlLCBWYWx1ZSwgZmlsbD1kZXZlbG9wbWVudF9jaGFuZ2UpKSArIA0KICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuLnkgPSAibWVhbiIpICsNCiAgICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArDQogICAgbGFicyh0aXRsZT0iTmV3IERldmVsb3BtZW50IGFzIGEgRnVuY3Rpb24gb2YgdGhlIENvbnRpbnVvdXMgVmFyaWFibGVzIikgKw0KICAgIHBsb3RUaGVtZQ0KDQpgYGANCg0KVGhpcyBuZXh0IHBsb3Qgc2hvd3MgdGhhdCBuZXcgZGV2ZWxvcG1lbnQgaGFzLCBvbiBhdmVyYWdlLCBvY2N1cnJlZCBpbiBjZWxscyB3aGVyZSB0aGUgcG9wdWxhdGlvbiBpcyBncmVhdGVyIG9yIGdyb3dpbmcuIEZyb20gdGhpcywgd2UgbGVhcm4gdGhhdCBkZXZlbG9wbWVudCBpbiBBdGxhbnRhIE1TQSBpcyBhbHNvIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHRoZSBhbW91bnQgb2YgcG9wdWxhdGlvbiBhbmQgcG9wdWxhdGlvbiBncm93dGggaW4gYW4gYXJlYS4NCg0KYGBge3IgYmFyIHBsb3RzIG9mIHBvcHVsYXRpb24gdmFyaWFibGVzfQ0KDQpkYXQgJT4lDQogIGRwbHlyOjpzZWxlY3QocG9wMDkscG9wMTkscG9wX0NoYW5nZSxkZXZlbG9wbWVudF9jaGFuZ2UpICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZGV2ZWxvcG1lbnRfY2hhbmdlLCAtZ2VvbWV0cnkpICU+JQ0KICBnZ3Bsb3QoLiwgYWVzKGRldmVsb3BtZW50X2NoYW5nZSwgVmFsdWUsIGZpbGw9ZGV2ZWxvcG1lbnRfY2hhbmdlKSkgKyANCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAic3VtbWFyeSIsIGZ1bi55ID0gIm1lYW4iKSArDQogICAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KICAgIGxhYnModGl0bGU9Ik5ldyBEZXZlbG9wbWVudCBhcyBhIEZ1bmN0aW9uIG9mIEZhY3RvciBWYXJpYWJsZXMiKSArDQogICAgcGxvdFRoZW1lDQoNCmBgYA0KDQpGcm9tIHRoZSBhYm92ZSBkYXRhIGV4cGxvcmF0aW9uLCB3ZSBjYW4gc2VlIHRoYXQgZGV2ZWxvcG1lbnQgaXMgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggcHJveGltaXR5IHRvIGhpZ2h3YXlzIGFuZCBkZXZlbG9wbWVudCwgYXMgd2VsbCBhcyB3aXRoIGV4aXN0aW5nIGFuZCBncm93aW5nIHBvcHVsYXRpb24uIEhvd2V2ZXIsIHRoaXMgbGVhdmVzIHVzIHdvbmRlcmluZyB3aGljaCB0eXBlcyBvZiBsYW5kIGNvdmVyIGFyZSBtb3N0IGxpa2VseSB0byBjb252ZXJ0IHRvIGRldmVsb3BlZCBsYW5kPw0KDQpUaGUgY29udmVyc2lvbiB0YWJsZSBzaG93cyB1cyB0aGF0IGZvcmVzdGVkIGFyZWFzIGluIDIwMDggd2VyZSB0aGUgbW9zdCBsaWtlbHkgdG8gaGF2ZSBiZWVuIGRldmVsb3BlZCBieSAyMDE5LiBUaGlzIHNob3dzIHRoYXQgQXRsYW50YSBoYXMgYSBwcmVmZXJlbmNlIGZvciBkZXZlbG9waW5nIHdvb2RsYW5kcyBmaXJzdCwgd2l0aCBhcmVhcyBhZGphY2VudCB0byB0aG9zZSB0aGF0IGFyZSBhbHJlYWR5IGRldmVsb3BlZCBhcyBhIGNsb3NlIHNlY29uZC4NCg0KYGBge3IgbGFuZCBjb3ZlciBjb252ZXJzaW9uIHRhYmxlcywgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9Nn0NCmRhdCAlPiUNCiAgZHBseXI6OnNlbGVjdChkZXZlbG9wbWVudF9jaGFuZ2U6b3RoZXJVbmRldmVsb3BlZDA4LGRldmVsb3BlZDA4KSAlPiUNCiAgZ2F0aGVyKExhbmRfQ292ZXJfVHlwZSwgVmFsdWUsIC1kZXZlbG9wbWVudF9jaGFuZ2UsIC1nZW9tZXRyeSkgJT4lDQogICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lDQogICAgIGdyb3VwX2J5KGRldmVsb3BtZW50X2NoYW5nZSwgTGFuZF9Db3Zlcl9UeXBlKSAlPiUNCiAgICAgc3VtbWFyaXplKG4gPSBzdW0oYXMubnVtZXJpYyhWYWx1ZSkpKSAlPiUNCiAgICAgdW5ncm91cCgpICU+JQ0KICAgIG11dGF0ZShDb252ZXJzaW9uX1JhdGUgPSBwYXN0ZTAocm91bmQoMTAwICogbi9zdW0obiksIDIpLCAiJSIpKSAlPiUNCiAgICBmaWx0ZXIoZGV2ZWxvcG1lbnRfY2hhbmdlID09IDEpICU+JQ0KICBkcGx5cjo6c2VsZWN0KExhbmRfQ292ZXJfVHlwZSxDb252ZXJzaW9uX1JhdGUpICU+JQ0KICByZWFjdGFibGUodGhlbWUgPSByZWFjdGFibGVUaGVtZShzdHlsZSA9IGxpc3QoZm9udEZhbWlseSA9ICJBcmlhbCwgc2Fucy1zZXJpZiIsIGZvbnRTaXplID0gIjEuMHJlbSIpKSkNCmBgYA0KDQo8YnI+DQoNCiMgQmluYXJ5IExvZ2lzdGljIFJlZ3Jlc3Npb24gLSBQcmVkaWN0aW5nIGZvciAyMDE5DQoNCk5vdyB0aGF0IHdlIGhhdmUgZ2F0aGVyZWQgYSBudW1iZXIgb2YgZmFjdG9ycyB3ZSBoeXBvdGhlc2l6ZWQgdG8gYmUgcmVsYXRlZCB0byBuZXcgZGV2ZWxvcG1lbnQsIGFuZCBoYXZlIHByb3ZlZCB0aGF0IHRoZXkgaGF2ZSBhdCBsZWFzdCBzb21lIGFzc29jaWF0aW9uIHdpdGggZGV2ZWxvcG1lbnQsIHdlIGNhbiBidWlsZCBhbmQgdHJhaW4gYSBtb2RlbCB0byBwcmVkaWN0IG5ldyBkZXZlbG9wbWVudCBiYXNlZCBvbiB0aGVtLg0KDQpXZSBjaG9zZSB0byB1c2UgYmlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbiBmb3IgdGhpcyBhc3NpZ25tZW50IGJlY2F1c2UgaXQgaXMgYSB0eXBlIG9mIHN0YXRpc3RpY2FsIG1vZGVsIHRoYXQgaXMgd2VsbCBzdWl0ZWQgdG8gYW5hbHl6aW5nIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBhIGJpbmFyeSByZXNwb25zZSB2YXJpYWJsZSBhbmQgb25lIG9yIG1vcmUgcHJlZGljdG9yIHZhcmlhYmxlcy4gSXQgZXN0aW1hdGVzIHRoZSBwcm9iYWJpbGl0eSBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgdGFraW5nIG9uZSBvZiB0d28gcG9zc2libGUgdmFsdWVzIChpbiB0aGlzIGNhc2UgZGV2ZWxvcGVkIG9yIG5vdCBkZXZlbG9wZWQpIGJhc2VkIG9uIHRoZSB2YWx1ZXMgb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMuDQoNClRvIHByZWRpY3QgdGhlIGxpa2VsaWhvb2Qgb2YgZnV0dXJlIGRldmVsb3BtZW50LCB3ZSB1c2UgZGF0YSBmcm9tIDIwMDgvMjAwOSB0byBjcmVhdGUgYSBzZXJpZXMgb2YgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgdG8gcHJlZGljdCBsYW5kIHVzZSBjaGFuZ2UgaW4gMjAxOS4gV2UgdGhlbiBldmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlc2UgbW9kZWxzIHRvIHNlbGVjdCBvdXIgYmVzdCBtb2RlbCBmb3IgcHJlZGljdGluZyBkZXZlbG9wbWVudCBpbiAyMDI5Lg0KDQojIyBNb2RlbCBCdWlsZGluZw0KDQpUaGUgZmlyc3Qgc3RlcCBvZiB0aGlzIHByb2Nlc3MgaXMgcmFuZG9tbHkgc3BsaXR0aW5nIHRoZSBkYXRhIGluIGhhbGYgdG8gY3JlYXRlIGEgdGVzdGluZyBkYXRhIHNldCB3aXRoIG9uZSBoYWxmIGFuZCBhIHRyYWluaW5nIGRhdGEgc2V0IHdpdGggdGhlIG90aGVyLg0KDQpgYGB7ciB0cmFpbmluZyBzZXRzLCByZXN1bHRzPSdoaWRlJ30NCnNldC5zZWVkKDM0NTYpDQp0cmFpbkluZGV4IDwtIA0KICBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdCRkZXZlbG9wZWQwOCwgcCA9IC41MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSAxKQ0KZGF0VHJhaW4gPC0gZGF0WyB0cmFpbkluZGV4LF0NCmRhdFRlc3QgIDwtIGRhdFstdHJhaW5JbmRleCxdDQoNCmBgYA0KDQpXZSB0aGVuIGJ1aWxkIGEgc2VyaWVzIG9mIGJpbmFyeSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBiYXNlZCBvbiBvdXIgaW5kZXBlbmRlbnQgdmFyaWFibGVzLg0KDQpUaGUgc2l4IG1vZGVscyB3ZSBjcmVhdGVkIGFyZSBudW1iZXJlZCB0byByZWZsZWN0IGluY3JlYXNpbmcgY29tcGxleGl0eS4gV2hpbGUgTW9kZWwgMSBjb25jZW50cmF0ZXMgZXhjbHVzaXZlbHkgb24gbGFuZCBjb3ZlciB0eXBlIGFuZCB3aGV0aGVyIGRldmVsb3BtZW50IGNoYW5nZSBvY2N1cnJlZCBiZXR3ZWVuIHRoZSB0d28gdGltZSBwZXJpb2RzLCBsYXRlciBtb2RlbHMgaW5jb3Jwb3JhdGUgdGhlIHNwYXRpYWwgbGFnIG9mIGRldmVsb3BtZW50LCBwb3B1bGF0aW9uIGVsZW1lbnRzLCBhbmQgb3VyIGRpc3RhbmNlIHZhcmlhYmxlcy4NCg0KYGBge3IgbGluZWFyIG1vZGVscyBhbmQgcGxvdHRpbmcgdGhlaXIgUiBzcWF1cmVkIHZhbHVlcyBhbmQgdGVzdCBzZXQgaGlzdG9ncmFtIHBsb3R9DQpNb2RlbDEgPC0gZ2xtKGRldmVsb3BtZW50X2NoYW5nZSB+IHdldGxhbmRzMDggKyBmb3Jlc3QwOCAgKyBmYXJtMDggKyBvdGhlclVuZGV2ZWxvcGVkMDgsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pDQoNCk1vZGVsMiA8LSBnbG0oZGV2ZWxvcG1lbnRfY2hhbmdlIH4gd2V0bGFuZHMwOCArIGZvcmVzdDA4ICArIGZhcm0wOCArIG90aGVyVW5kZXZlbG9wZWQwOCArIGxhZ0RldmVsb3BtZW50MDgsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pDQogICAgICAgICAgICAgIA0KTW9kZWwzIDwtIGdsbShkZXZlbG9wbWVudF9jaGFuZ2UgfiB3ZXRsYW5kczA4ICsgZm9yZXN0MDggICsgZmFybTA4ICsgb3RoZXJVbmRldmVsb3BlZDA4ICsgbGFnRGV2ZWxvcG1lbnQwOCArIHBvcDA5LCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICANCiAgICAgICAgICAgICAgDQpNb2RlbDQgPC0gZ2xtKGRldmVsb3BtZW50X2NoYW5nZSB+IHdldGxhbmRzMDggKyBmb3Jlc3QwOCAgKyBmYXJtMDggKyBvdGhlclVuZGV2ZWxvcGVkMDggKyBsYWdEZXZlbG9wbWVudDA4ICsgcG9wMDksIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAgICAgICAgICAgICANCiAgICAgICAgICAgIA0KTW9kZWw1IDwtIGdsbShkZXZlbG9wbWVudF9jaGFuZ2UgfiB3ZXRsYW5kczA4ICsgZm9yZXN0MDggICsgZmFybTA4ICsgb3RoZXJVbmRldmVsb3BlZDA4ICsgbGFnRGV2ZWxvcG1lbnQwOCArIHBvcDA5ICsgcG9wX0NoYW5nZSwNCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIA0KTW9kZWw2IDwtIGdsbShkZXZlbG9wbWVudF9jaGFuZ2UgfiB3ZXRsYW5kczA4ICsgZm9yZXN0MDggICsgZmFybTA4ICsgb3RoZXJVbmRldmVsb3BlZDA4ICsgbGFnRGV2ZWxvcG1lbnQwOCArIHBvcF9DaGFuZ2UgKyBwb3AwOSArIGRpc3RhbmNlX2hpZ2h3YXlzLCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSANCg0KDQptb2RlbExpc3QgPC0gcGFzdGUwKCJNb2RlbCIsIDE6NikNCg0KbWFwX2RmYyhtb2RlbExpc3QsIGZ1bmN0aW9uKHgpcFIyKGdldCh4KSkpWzQsXSAlPiUNCiAgc2V0TmFtZXMocGFzdGUwKCJNb2RlbCIsMTo2KSkgJT4lDQogIGdhdGhlcihNb2RlbCxNY0ZhZGRlbikgJT4lDQogIGdncGxvdChhZXMoTW9kZWwsTWNGYWRkZW4pKSArDQogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsID0gaGlnaGxpZ2h0KSArDQogICAgbGFicyh0aXRsZT0gIk1jRmFkZGVuIFItU3F1YXJlZCBieSBNb2RlbCIpICsNCiAgICBwbG90VGhlbWUNCg0KYGBgDQoNCldlIHRoZW4gZXhhbWluZSB0aGUgTWNGYWRkZW4gUi1TcXVhcmVkIG9mIHRoZSBzaXggbW9kZWxzIHRvIHNlbGVjdCBvdXIgYmVzdCBmaW5hbCBtb2RlbC4gTW9kZWwgMSBjbGVhcmx5IHBlcmZvcm1zIG1vcmUgcG9vcmx5IHRoYW4gdGhlIG90aGVycy4gSG93ZXZlciwgbW9kZWxzIDItNCBhbHNvIHBlcmZvcm0gbW9kZXJhdGVseSBsZXNzIHdlbGwgdGhhbiB0aGUgcmVtYWluaW5nIG1vZGVscy4gVGhlcmVmb3JlLCB3ZSBlbGltaW5hdGUgbW9kZWxzIDEtNCBhbmQgY2hvb3NlIE1vZGVsIDYgYXMgb3VyIGZpbmFsIG1vZGVsIGJlY2F1c2UgaXQgcGVyZm9ybXMgdGhlIGJlc3QgYW5kIGFsbG93cyB1cyB0byBleHBsb3JlIHRoZSBlZmZlY3RzIG9mIGFsbCBvZiBvdXIgdmFyaWFibGVzIG9mIGludGVyZXN0Lg0KDQpXZSB0aGVuIHVzZSB0aGUgcHJlZGljdGVkIHZhbHVlcyBmcm9tIE1vZGVsIDYgdG8gY3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSBtb2RlbCdzIHRlc3Qgc2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzLiBUaGlzIHNob3dzIHVzIHRoYXQgYSBoaWdoIGRlbnNpdHkgb2YgY2VsbHMgaGF2ZSByb3VnaGx5IGEgNDAlIGxpa2VsaWhvb2Qgb2YgZGV2ZWxvcG1lbnQuIFRoaXMgbWFrZXMgc2Vuc2UgZ2l2ZW4gdGhlIHNwcmF3bGluZyBuYXR1cmUgb2YgdGhlIEF0bGFudGEgTVNBJ3MgZXhpc3RpbmcgZGV2ZWxvcG1lbnQgcGF0dGVybnMsIHdoaWNoIGFyZSBhY3JlYWdlIGludGVuc2l2ZS4gSXQgYWxzbyBtYWtlcyBzZW5zZSBnaXZlbiB0aGUgbWFwIG9mIG5ldyBkZXZlbG9wbWVudCB3ZSBwbG90dGVkIGVhcmxpZXIsIGluIHdoaWNoIGl0IHNlZW1zIGZlYXNpYmxlIHRoYXQgcm91Z2hseSBcfjMwLTQwJSBvZiB0aGUgTVNBIHdhcyBjb3ZlcmVkIGJ5IG5ldyBkZXZlbG9wbWVudCBpbiAyMDE5Lg0KDQpgYGB7ciBEZW5zaXR5IHBsb3Qgb2YgcHJvYmFiaWxpdGllcyBvYnNlcnZlZCBieSBjbGFzc30NCnRlc3RTZXRQcm9icyA8LSANCiAgZGF0YS5mcmFtZShjbGFzcyA9IGRhdFRlc3QkZGV2ZWxvcG1lbnRfY2hhbmdlLA0KICAgICAgICAgICAgIHByb2JzID0gcHJlZGljdChNb2RlbDYsIGRhdFRlc3QsIHR5cGU9InJlc3BvbnNlIikpIA0KICANCmdncGxvdCh0ZXN0U2V0UHJvYnMsIGFlcyhwcm9icykpICsNCiAgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsPWNsYXNzKSwgYWxwaGE9MC43NSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArDQogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIHRlc3Qgc2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIiwNCiAgICAgICB4PSJQcmVkaWN0ZWQgUHJvYmFiaWxpdGllcyIseT0iRGVuc2l0eSIpICsNCiAgcGxvdFRoZW1lDQoNCmBgYA0KDQojIyBBY2N1cmFjeQ0KDQpUaGlzIG5leHQgc2VnbWVudCB0ZXN0cyBvdXIgbW9kZWwnczoNCg0KMSkgIFNlbnNpdGl2aXR5OiB0aGUgZXh0ZW50IHRvIHdoaWNoIGl0IGFjY3VyYXRlbHkgcHJlZGljdHMgbmV3IGRldmVsb3BtZW50LCBvciAidHJ1ZSBwb3NpdGl2ZXMiLCBhbmQ6DQoyKSAgU3BlY2lmaWNpdHksIHRoZSBleHRlbnQgdG8gd2hpY2ggaXQgYWNjdXJhdGVseSBwcmVkaWN0cyAidHJ1ZSBuZWdhdGl2ZXMuIg0KDQpPdXIgdWx0aW1hdGUgZ29hbCB3aXRoIGEgbGFuZCBjb3ZlciBjaGFuZ2UgbW9kZWwgaXMgdG8gb3B0aW1pemUgdGhlIGJhbGFuY2UgYmV0d2VlbiB0aGUgbW9kZWwncyBwcmVkaWN0aW9ucyB3aXRoIHJlZ2FyZCB0byBib3RoIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eS4gTWVhbmluZywgd2Ugd2FudCB0aGUgbW9kZWwgdG8gYmUgY2FwYWJsZSBvZiBwcmVkaWN0aW5nIHdoZXJlIGRldmVsb3BtZW50IG9jY3VycyBzbyB0aGF0IHdlIGNhbiBwbGFuIGZvciB0aG9zZSBhcmVhcywgYnV0IHdlIGFsc28gd2FudCBpdCB0byBhY2N1cmF0ZWx5IHByZWRpY3Qgd2hlcmUgZGV2ZWxvcG1lbnQgd291bGQgbm90IG9jY3VyLCB0byBpbmZvcm0gb3VyIG90aGVyIGxhbmQgdXNlIHBsYW5uaW5nLg0KDQpBdCB0aGUgZmlyc3QgdHdvIHRocmVzaG9sZHMgc3BlY2lmaWVkIGJlbG93LCA1JSBhbmQgMTclLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIDUlIHRocmVzaG9sZCB2ZXJ5IGFjY3VyYXRlbHkgcHJlZGljdHMgdGhlICJ0cnVlIG5lZ2F0aXZlcywiIHdoZXJlIGRldmVsb3BtZW50IGRvZXMgbm90IG9jY3VyLiBUaGUgdGhyZXNob2xkIG9mIDE3JSwgaG93ZXZlciwgcGVyZm9ybXMgYmV0dGVyIG9uIHRoZSAidHJ1ZSBwb3NpdGl2ZSIgcmF0ZSBvZiBwcmVkaWN0ZWQgdi4gYWN0dWFsIGRldmVsb3BtZW50LCB0aGF0IGhhcyBvY2N1cnJlZC4gV2UgZm91bmQsIGhvd2V2ZXIsIHRoYXQgYSB0aHJlc2hvbGQgb2YgYXJvdW5kIDM0JSBnaXZlcyB0aGUgYmVzdCBiYWxhbmNlIGJldHdlZW4gdGhlIHRyYWRlLW9mZnMgb2Ygc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5LiBXZSBjaG9zZSAzNCUgYmVjYXVzZSBpdCBnaXZlcyB1cyB0aGUgaGlnaGVzdCBvdmVyYWxsIGFjY3VyYWN5LCBhbmQgYWxsb3dzIHVzIHRvIGtlZXAgYm90aCBvdXIgc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5IG92ZXIgNTAlLg0KDQpgYGB7ciB0ZXN0aW5nIG1vZGVsIHNlbnNpdGl2aXR5LCBmaWcuaGVpZ2h0PTZ9DQpvcHRpb25zKHlhcmRzdGljay5ldmVudF9maXJzdCA9IEZBTFNFKQ0KDQp0ZXN0U2V0UHJvYnMgPC0gDQogIHRlc3RTZXRQcm9icyAlPiUgDQogIG11dGF0ZShwcmVkQ2xhc3NfMDUgPSBhcy5mYWN0b3IoaWZlbHNlKHRlc3RTZXRQcm9icyRwcm9icyA+PSAwLjA1ICwxLDApKSwNCiAgICAgICAgIHByZWRDbGFzc18xNyA9IGFzLmZhY3RvcihpZmVsc2UodGVzdFNldFByb2JzJHByb2JzID49IDAuMTcgLDEsMCkpLA0KICAgICAgICAgcHJlZENsYXNzXzM0ID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMkcHJvYnMgPj0gMC4zNCAsMSwwKSkpIA0KDQp0ZXN0U2V0UHJvYnMgJT4lDQogIGRwbHlyOjpzZWxlY3QoLXByb2JzKSAlPiUNCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWNsYXNzKSAlPiUNCiAgZ3JvdXBfYnkoVmFyaWFibGUpICU+JQ0KICBzdW1tYXJpemUoU2Vuc2l0aXZpdHkgPSByb3VuZCh5YXJkc3RpY2s6OnNlbnNfdmVjKGNsYXNzLGZhY3RvcihWYWx1ZSkpLDIpLA0KICAgICAgICAgICAgU3BlY2lmaWNpdHkgPSByb3VuZCh5YXJkc3RpY2s6OnNwZWNfdmVjKGNsYXNzLGZhY3RvcihWYWx1ZSkpLDIpLA0KICAgICAgICAgICAgQWNjdXJhY3kgPSByb3VuZCh5YXJkc3RpY2s6OmFjY3VyYWN5X3ZlYyhjbGFzcyxmYWN0b3IoVmFsdWUpKSwyKSkgJT4lIA0KICByZWFjdGFibGUodGhlbWUgPSByZWFjdGFibGVUaGVtZShzdHlsZSA9IGxpc3QoZm9udEZhbWlseSA9ICJBcmlhbCwgc2Fucy1zZXJpZiIsIGZvbnRTaXplID0gIjEuMHJlbSIpKSkNCg0KYGBgDQoNCldlIHRoZW4gY29udmVydCB0aGVzZSB0cnVlL2ZhbHNlIHBvc2l0aXZlL25lZ2F0aXZlIGluZGljYXRvcnMgdG8gZmFjdG9ycyBpbiBvcmRlciB0byBtYXAgdGhlbS4NCg0KYGBge3IgZGV2ZWxvcG1lbnQgcHJlZGljdGlvbnMgYmFzZWQgb24gdGhyZXNob2xkcywgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQ0KcHJlZHNGb3JNYXAgPC0gICAgICAgICANCiAgZGF0ICU+JQ0KICAgIG11dGF0ZShwcm9icyA9IHByZWRpY3QoTW9kZWw2LCBkYXQsIHR5cGU9InJlc3BvbnNlIikgLA0KICAgICAgICAgICBUaHJlc2hvbGRfNV9QY3QgPSBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMDUgLDEsMCkpLA0KICAgICAgICAgICBUaHJlc2hvbGRfMTdfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4xNyAsMSwwKSksDQogICAgICAgICAgIFRocmVzaG9sZF8zNF9QY3QgPSAgYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjM0ICwxLDApKSkgJT4lDQogICAgZHBseXI6OnNlbGVjdChkZXZlbG9wbWVudF9jaGFuZ2UsVGhyZXNob2xkXzVfUGN0LFRocmVzaG9sZF8xN19QY3QsIFRocmVzaG9sZF8zNF9QY3QpICU+JQ0KICAgIGdhdGhlcihWYXJpYWJsZSxWYWx1ZSwgLWdlb21ldHJ5KSAlPiUNCiAgICBzdF9jYXN0KCJQT0xZR09OIikNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGRhdGE9cHJlZHNGb3JNYXAsIGFlcyh4PXh5QyhwcmVkc0Zvck1hcClbLDFdLCB5PXh5QyhwcmVkc0Zvck1hcClbLDJdLCBjb2xvdXI9VmFsdWUpKSArDQogIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhncmF5LCBoaWdobGlnaHQpLCBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMsDQogICAgICAgICAgY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC41KSArDQogIGdlb21fc2YoZGF0YT1BdGxhbnRhX01TQSwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGU9IkRldmVsb3BtZW50IFByZWRpY3Rpb25zIC0gTG93IFRocmVzaG9sZCIpICsgDQogIG1hcFRoZW1lICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCmBgYA0KDQpUaGVzZSBtYXBzIHNob3cgdGhhdCB0aGUgMzQlIG5pY2VseSBjYXB0dXJlcyB0aGUgTVNBJ3Mgb3ZlcmFsbCBwYXR0ZXJuIG9mIGRldmVsb3BtZW50IHdoaWxlIG1pbmltaXppbmcgdGhlIHRlbmRlbmN5IHRvIG92ZXItcHJlZGljdCBkZXZlbG9wbWVudCB0aGF0IHRoZSAxNyUgYW5kIDUlIHRocmVzaG9sZHMgc2hhcmUgKHRoZSA1JSB0aHJlc2hvbGQgd2lsZGx5IHNvKS4NCg0KVG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIHRyYWRlLW9mZnMgYmV0d2VlbiBhY2N1cmF0ZWx5IHByZWRpY3RpbmcgZmFsc2UgcG9zaXRpdmVzIGFuZCBmYWxzZSBuZWdhdGl2ZXMsIHdlIGNhbiBydW4gb3VyIHByZWRpY3Rpb25zIHRocm91Z2ggYSBjb25mdXNpb24gbWF0cml4IHRvIHZpc3VhbGl6ZSB0aGUgdHJ1ZSBwb3NpdGl2ZXMgKCJUcnVlUCIpIGFuZCB0cnVlIG5lZ2F0aXZlcyAoIlRydWVOIikgaW4gdGhlIGNoYXJ0cyBiZWxvdywgZm9yIGVhY2ggb2YgdGhlIHRocmVzaG9sZHMuDQoNCmBgYHtyIGRldmVsb3BtZW50IHByZWRpY3Rpb25zIGJhc2VkIG9uIGNvbmZ1c2lvbiBtZXRyaWNzLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQpkYXQgPC0gZGF0ICU+JSANCiAgbXV0YXRlKHByb2JzID0gcHJlZGljdChNb2RlbDYsIGRhdCwgdHlwZT0icmVzcG9uc2UiKSkNCiAgDQpDb25mdXNpb25NYXRyaXgubWV0cmljcyA8LQ0KICBkYXQgJT4lDQogICAgbXV0YXRlKFRocmVzaG9sZF81X1BjdCA9IGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4wNSAsMSwwKSksDQogICAgICAgICAgIFRocmVzaG9sZF8xN19QY3QgPSAgYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjE3ICwxLDApKSwNCiAgICAgICAgICBUaHJlc2hvbGRfMzRfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4zNCAsMSwwKSkpICU+JQ0KICAgIG11dGF0ZShUcnVlUF8wNSA9IGlmZWxzZShkZXZlbG9wbWVudF9jaGFuZ2UgID09IDEgJiBUaHJlc2hvbGRfNV9QY3QgPT0gMSwgMSwwKSwNCiAgICAgICAgICAgVHJ1ZU5fMDUgPSBpZmVsc2UoZGV2ZWxvcG1lbnRfY2hhbmdlICA9PSAwICYgVGhyZXNob2xkXzVfUGN0ID09IDAsIDEsMCksDQogICAgICAgICAgIFRydWVQXzE3ID0gaWZlbHNlKGRldmVsb3BtZW50X2NoYW5nZSAgPT0gMSAmIFRocmVzaG9sZF8xN19QY3QgPT0gMSwgMSwwKSwNCiAgICAgICAgICAgVHJ1ZU5fMTcgPSBpZmVsc2UoZGV2ZWxvcG1lbnRfY2hhbmdlICA9PSAwICYgVGhyZXNob2xkXzE3X1BjdCA9PSAwLCAxLDApLA0KICAgICAgICAgICBUcnVlUF8zNCA9IGlmZWxzZShkZXZlbG9wbWVudF9jaGFuZ2UgID09IDEgJiBUaHJlc2hvbGRfMzRfUGN0ID09IDEsIDEsMCksDQogICAgICAgICAgIFRydWVOXzM0ID0gaWZlbHNlKGRldmVsb3BtZW50X2NoYW5nZSAgPT0gMCAmIFRocmVzaG9sZF8zNF9QY3QgPT0gMCwgMSwwKSkgJT4lDQogICAgZHBseXI6OnNlbGVjdCguLCBzdGFydHNfd2l0aCgiVHJ1ZSIpKSAlPiUNCiAgICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZ2VvbWV0cnkpICU+JQ0KICAgIHN0X2Nhc3QoIlBPTFlHT04iKSANCg0KZ2dwbG90KGRhdGE9Q29uZnVzaW9uTWF0cml4Lm1ldHJpY3MpICsNCiAgZ2VvbV9wb2ludChhZXMoeD14eUMoQ29uZnVzaW9uTWF0cml4Lm1ldHJpY3MpWywxXSwgDQogICAgICAgICAgICAgICAgIHk9eHlDKENvbmZ1c2lvbk1hdHJpeC5tZXRyaWNzKVssMl0sIGNvbG91ciA9IGFzLmZhY3RvcihWYWx1ZSkpKSArDQogIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhncmF5LCBoaWdobGlnaHQpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJJbmNvcnJlY3QiLCJDb3JyZWN0IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMsDQogICAgICAgICAgY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC41KSArDQogIGdlb21fc2YoZGF0YT1BdGxhbnRhX01TQSwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGU9IkRldmVsb3BtZW50IFByZWRpY3Rpb25zIC0gTG93IFRocmVzaG9sZCIpICsNCiAgbWFwVGhlbWUgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KYGBgDQoNCk1hcHBpbmcgdGhlIHNlbnNpdGl2aXR5ICh0cnVlIHBvc2l0aXZlcykgYW5kIHNwZWNpZmljaXR5ICh0cnVlIG5lZ2F0aXZlcykgb2YgdGhlc2UgdGhyZXNob2xkcyBzaG93cyBpbiBmdXJ0aGVyIGRldGFpbCB0aGF0IHRoZSAzNCUgdGhyZXNob2xkIHByZWRpY3RzIG5vdGljZWFibHkgdHJ1ZSBuZWdhdGl2ZXMgdGhhbiB0aGUgb3RoZXIgdGhyZXNob2xkcyB3aGlsZSBwcmVkaWN0aW5nIG9ubHkgc2xpZ2h0bHkgbGVzcyB0cnVlIHBvc2l0aXZlcy4gVGhlcmVmb3JlLCB3ZSBzZWxlY3QgMzQlIGFzIHRoZSBwcm9iYWJpbGl0eSB0aHJlc2hvbGQgYXQgd2hpY2ggdG8gY2xhc3NpZnkgbGFuZCBhcyBsaWtlbHkgdG8gYmUgZGV2ZWxvcGVkIGluIHRoZSByZW1haW5kZXIgb2YgdGhpcyBwcm9qZWN0Lg0KDQojIFBsYW5uaW5nIFNjZW5hcmlvcw0KDQpOb3cgdGhhdCB3ZSBoYXZlIGRldmVsb3BlZCBhIHByZWRpY3RpdmUgbW9kZWwgZm9yIG5ldyBkZXZlbG9wbWVudCwgd2UgY2FuIHVzZSBpdCB0byB0ZXN0IHRoZSBlZmZlY3RzIG9mIHBvdGVudGlhbCBmdXR1cmUgc2NlbmFyaW9zLiBXZSBmaXJzdCB1c2UgdGhlIG1vZGVsIHRvIHByZWRpY3QgaG93IGEgZGVtYW5kLXNpZGUgY2hhbmdlIChwcm9qZWN0ZWQgcG9wdWxhdGlvbikgbWlnaHQgYWZmZWN0IGRldmVsb3BtZW50IGluIDIwMjkuIFdlIHRoZW4gdXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IGhvdyBhIHN1cHBseS1zaWRlIGNoYW5nZSAoYSBuZXcgaGlnaHdheSkgbWlnaHQgYWZmZWN0IGRldmVsb3BtZW50DQoNCiMjIFNjZW5hcmlvIDE6IERlbWFuZC1zaWRlIENoYW5nZSBGb3JlY2FzdA0KDQpJbiB0aGlzIGRlbWFuZC1zaWRlIHByb2plY3Rpb24sIHdlIHdpbGwgdXNlIHBvcHVsYXRpb24gcHJvamVjdGlvbnMgZm9yIHRoZSBBdGxhbnRhIE1TQSBjb3VudGllcyBhbmQgZGlzdHJpYnV0ZSB0aGUgcG9wdWxhdGlvbiBhbW9uZyBvdXIgZmlzaG5ldCBjZWxscy4gVGhlIGdvYWwgaXMgdG8gdmlzdWFsaXplIGNoYW5nZSBhbmQgcGxhbiBmb3IgZ3Jvd3RoIHNjZW5hcmlvcyBiYXNlZCBvbiBob3cgYW50aWNpcGF0ZWQgZnV0dXJlIHBvcHVsYXRpb24gbWlnaHQgZHJpdmUgZGV2ZWxvcG1lbnQuDQoNCkZpcnN0LCB3ZSByZXBsYWNlIHRoZSBsYWcgZGV2ZWxvcG1lbnQgMjAwOCBjb2x1bW4gd2l0aCB0aGUgZGlzdGFuY2UgZnJvbSBkZXZlbG9wbWVudCBpbiAyMDE5Lg0KDQpgYGB7ciBsYWcgZGV2IDIwMTl9DQpkYXQgPC0NCiAgZGF0ICU+JQ0KICBtdXRhdGUobGFnRGV2ZWxvcG1lbnQwOCA9IG5uX2Z1bmN0aW9uKHh5QyguKSwgeHlDKGZpbHRlciguLCBkZXZlbG9wZWQxOSA9PSAxKSksMikpDQpgYGANCg0KTmV4dCwgd2UgbG9vayBhdCBwcm9qZWN0ZWQgcG9wdWxhdGlvbiBieSBjb3VudHkgaW4gMjAyOSBiYXNlZCBvbiBwb3B1bGF0aW9uIGVzdGltYXRlcyBmcm9tIHRoZSBbR2VvcmdpYSBEYXRhIEFuYWx5dGljcyBDZW50ZXJdKGh0dHBzOi8vZ2RhYy5nZW9yZ2lhLmdvdi9jZW5zdXMpLg0KDQpgYGB7ciBwcm9qZWN0ZWQgcG9wIDIwMjl9DQojQ291bnR5IHByb2plY3Rpb25zIDIwMjkNCiNCYXJyb3cgOTY5NzcgLQ0KI0JhcnRvdyAxMTk1OTQgLSANCiNDYXJyb2xsIDEzMTU3NyAtDQojQ2hlcm9rZWUgIDMwMTc1Mi0NCiNDbGF5dG9uICAzMjE0MTAtDQojQ29iYiAgODMwNjcxLQ0KI0Nvd2V0YSAgMTcyMzQxIC0NCiNEYXdzb24gICAgCTMyNTcyLQ0KI0RlS2FsYiAgODIyMTU0LQ0KI0RvdWdsYXMgIDE2MDYzNS0NCiNGYXlldHRlICAxMjg2MjctDQojRm9yc3l0aCAgIDMxNjE2NS0NCiNGdWx0b24gIDExOTgzMzQtDQojR3dpbm5ldHQgICAxMDQ0MDI2LQ0KI0hhbGwgICAJMjMyNzIwLQ0KI0hlbnJ5ICAgMjc4NzM5LQ0KI05ld3RvbiAxMzA1NTktDQojUGF1bGRpbmcgMjA4MTk2LQ0KI1JvY2tkYWxlIAk5NTU3NC0NCiNTcGFsZGluZyAgIDcwOTc0LQ0KI1dhbHRvbiAJMTA5MjA1LQ0KDQpjb3VudHlQb3B1bGF0aW9uXzIwMjkgPC0gDQogIGRhdGEuZnJhbWUoDQogICBOQU1FID0gQ291bnRpZXMsDQogICBjb3VudHlfcHJvamVjdGlvbl8yMDI5ID0gDQogICAgIGMoOTY5NzcsMTE5NTk0LDEzMTU3NywzMDE3NTIsMzIxNDEwLDgzMDY3MSwxNzIzNDEsMzI1NzIsODIyMTU0LDE2MDYzNSwxMjg2MjcsDQogICAgICAgMzE2MTY1LDExOTgzMzQsMTA0NDAyNiwyMzI3MjAsMjc4NzM5LDEzMDU1OSwyMDgxOTYsOTU1NzQsNzA5NzQsMTA5MjA1KSkgJT4lDQogICBsZWZ0X2pvaW4oDQogICAgIGRhdCAlPiUNCiAgICAgICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lDQogICAgICAgZ3JvdXBfYnkoTkFNRSkgJT4lDQogICAgICAgc3VtbWFyaXplKGNvdW50eV9wb3B1bGF0aW9uXzIwMTkgPSByb3VuZChzdW0ocG9wMTkpKSkpDQoNCmNvdW50eVBvcHVsYXRpb25fMjAyOSAlPiUNCiAgZ2F0aGVyKFZhcmlhYmxlLFZhbHVlLCAtTkFNRSkgJT4lDQogIGdncGxvdChhZXMocmVvcmRlcihOQU1FLC1WYWx1ZSksVmFsdWUpKSArDQogIGdlb21fYmFyKGFlcyhmaWxsPVZhcmlhYmxlKSwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIjIwMTkiLCIyMDI5IiksDQogICAgICAgICAgICAgICAgICAgIG5hbWU9IlBvcHVsYXRpb24iKSArDQogIGxhYnModGl0bGU9IlBvcHVsYXRpb24gQ2hhbmdlIGJ5IENvdW50eTogMjAxOSAtIDIwMjkiLA0KICAgICAgIHg9IkNvdW50eSIsIHk9IlBvcHVsYXRpb24iKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsNCiAgcGxvdFRoZW1lDQpgYGANCg0KV2Ugc2VlIHRoYXQgRnVsdG9uLCBHd2lubmV0dCwgRGVLYWxiLCBhbmQgQ29iYiBjb3VudGllcyBhcmUgcHJvamVjdGVkIHRvIGV4cGVyaWVuY2UgdGhlIGxhcmdlc3QgcG9wdWxhdGlvbiBpbmNyZWFzZXMgYmV0d2VlbiAyMDE5IGFuZCAyMDI5Lg0KDQojIyMjIFByZWRpY3RpbmcgRGV2ZWxvcG1lbnQgRGVtYW5kIChiYXNlZCBvbiBwcm9qZWN0ZWQgcG9wdWxhdGlvbikNCg0KTm93LCB3ZSB3aWxsIHRha2UgdGhlIGNvdW50eS1sZXZlbCBwb3B1bGF0aW9uIHByb2plY3Rpb25zIGFuZCBkaXN0cmlidXRlIHRoZW0gYWNyb3NzIHRoZSBzdHVkeSBhcmVhLiBXZSBkbyB0aGlzIGJ5IHdlaWdodGluZyBpdCBwcm9wb3J0aW9uYWxseSBhY2NvcmRpbmcgdG8gYSBncmlkIGNlbGwncyAyMDE5IHBvcHVsYXRpb24gYW5kIHRoZW4gc3VidHJhY3RpbmcgMjAxOSBwb3AgdG8gZ2V0IGBwb3BfY2hhbmdlYC4NCg0KYGBge3IgcHJlZGljdGVkIGRldmVsb3BtZW50IGRlbWFuZCAyMDI5fQ0KZGF0X2luZmlsbCA8LQ0KICBkYXQgJT4lDQogICNjYWxjdWxhdGUgcG9wdWxhdGlvbiBjaGFuZ2UNCiAgICBsZWZ0X2pvaW4oY291bnR5UG9wdWxhdGlvbl8yMDI5KSAlPiUNCiAgICBtdXRhdGUocHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wID0gcG9wMTkgLyBjb3VudHlfcG9wdWxhdGlvbl8yMDE5LA0KICAgICAgICAgICBwb3BfMjAyOS5pbmZpbGwgPSBwcm9wb3J0aW9uX29mX2NvdW50eV9wb3AgKiBjb3VudHlfcHJvamVjdGlvbl8yMDI5LA0KICAgICAgICAgICBwb3BfQ2hhbmdlID0gcm91bmQocG9wXzIwMjkuaW5maWxsIC0gcG9wMTkpLDIpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoLWNvdW50eV9wcm9qZWN0aW9uXzIwMjksIC1jb3VudHlfcG9wdWxhdGlvbl8yMDE5LCANCiAgICAgICAgICAgICAgICAgIC1wcm9wb3J0aW9uX29mX2NvdW50eV9wb3AsIC1wb3BfMjAyOS5pbmZpbGwpICU+JQ0KICAjcHJlZGljdCBmb3IgMjAyOQ0KICAgIG11dGF0ZShwcmVkaWN0XzIwMjkuaW5maWxsID0gcHJlZGljdChNb2RlbDYsLiAsIHR5cGU9InJlc3BvbnNlIikpDQoNCm1hcF9wcmVkMjkgPC0gDQpkYXRfaW5maWxsICU+JQ0KICBnZ3Bsb3QoKSArICANCiAgZ2VvbV9wb2ludChhZXMoeD14eUMoZGF0X2luZmlsbClbLDFdLCB5PXh5QyhkYXRfaW5maWxsKVssMl0sIGNvbG91ciA9IGZhY3RvcihudGlsZShwcmVkaWN0XzIwMjkuaW5maWxsLDUpKSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhkYXRfaW5maWxsLCJwcmVkaWN0XzIwMjkuaW5maWxsIiksMSw0KSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iJSBMaWtlbGlob29kXG5vZiBEZXZlbG9wbWVudCIpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzLCBmaWxsPU5BLCBjb2xvdXI9IndoaXRlIiwgc2l6ZT0uNzUpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGU9ICJEZXZlbG9wbWVudCBEZW1hbmQgaW4gMjAyOTogUHJlZGljdGVkIFByb2JhYmlsaXRpZXMiLA0KICAgIHN1YnRpdGxlID0gIkJhc2VkIG9uIHBvcHVsYXRpb24gY2hhbmdlIHByb2plY3Rpb25zIikrDQogIG1hcFRoZW1lDQoNCm1hcF9wcmVkMjkNCmBgYA0KDQpNYXBwaW5nIHByZWRpY3RlZCBkZXZlbG9wbWVudCBiYXNlZCBvbiBwcm9qZWN0ZWQgcG9wdWxhdGlvbiBjaGFuZ2UgZm9yIDIwMjkgZG9lc24ndCBzaG93IGFueSBodWdlIGNoYW5nZXMgZnJvbSB3aGVyZSBvdXIgbW9kZWwgcHJlZGljdGVkIGRldmVsb3BtZW50IHdvdWxkIGJlIGluIDIwMTkuIE5ldyBkZXZlbG9wbWVudCBpcyBzdGlsbCBwcmVkaWN0ZWQgdG8gbW9zdCBsaWtlbHkgb2NjdXIgaW4gdGhlIG91dGVyIGNvdW50aWVzLiBBbHRob3VnaCwgb3VyIG1vZGVsIGlzIHByZWRpY3RpbmcgaW5jcmVhc2VkIGRldmVsb3BtZW50IGluIHRoZSBub3J0aHdlc3QgYW5kIHNvdXRod2VzdCBjb3JuZXJzIG9mIHRoZSBtZXRybyB3aGljaCBpdCB3YXMgbm90IHByZWRpY3RpbmcgYmVmb3JlIHdlIGFkZGVkIGluIHRoZSAyMDI5IHBvcHVsYXRpb24gcHJvamVjdGlvbnMuDQoNCjxicj4NCg0KIyMgU2NlbmFyaW8gMjogU3VwcGx5LXNpZGUgQ2hhbmdlIEZvcmVjYXN0DQoNClRvIGJlZ2luIG91ciBzdXBwbHktc2lkZSBwcm9qZWN0aW9uLCB3ZSBpbWFnaW5lIGEgbmV3IGhpZ2h3YXkgaW4gV2FsdG9uIENvdW50eSwgaW4gdGhlIGVhc3Rlcm4gcGFydCBvZiB0aGUgbWV0cm8sIHdoZXJlIGRldmVsb3BtZW50IGJldHdlZW4gMjAwOCBhbmQgMjAxOSB3YXMgbGVzcyBjb25jZW50cmF0ZWQuIFRoaXMgd2FzIGFsc28gYSBmb3Jlc3RlZCBhcmVhLCBzbyB0aGUgZmFjdCB0aGF0IG11Y2ggZGV2ZWxvcG1lbnQgaGFkIG5vdCBoYXBwZW5lZCB0aGVyZSBvdmVyIHRoZSBlYXJsaWVyIHBlcmlvZCBhcm91c2VkIG91ciBjdXJpb3NpdHkuIFRoaXMgd2F5LCB3ZSBjYW4gdGVzdCB0aGUgZXh0ZW50IHRoYXQgYSBuZXcgcm9hZCBpbnZlc3RtZW50IGluZmx1ZW5jZXMgZGV2ZWxvcG1lbnQgaW4gYSBkZXNpcmFibGUgYXJlYSBvZiBsYW5kIGNvdmVyLiBUaGlzIG5ldyByb2FkIHdhcyBjcmVhdGVkIGFzIGEgbGluZSBzZWdtZW50IGluIEFyY0dJUyBhbmQgbWVyZ2VkIHdpdGggdGhlIGV4aXN0aW5nIGhpZ2h3YXlzIHNoYXBlZmlsZS4NCg0KYGBge3IgaW1wb3J0IG5ldyByb2FkcyBmaWxlcywgcmVzdWx0cz0naGlkZSd9DQojTkVXIEhJR0hXQVkgU0VHTUVOVCBPTkxZDQpuZXdfaGlnaHdheSA8LSBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYy10b3duc2xleS9MVUVNX0E1X1JlcG8vYTVhNWIwYzkwOTliOTdmNjhjMGQxYWVhZGNkZmU5ZGJkMjk2MGE5YS9EYXRhL2dlb2pzb24vbmV3X2hpZ2h3YXkuZ2VvanNvbiIpDQoNCm5ld19oaWdod2F5IDwtIG5ld19oaWdod2F5ICU+JSANCnN0X3RyYW5zZm9ybShzdF9jcnMobWV0cm9fY291bnRpZXMpKSAlPiUNCiAgc3RfaW50ZXJzZWN0aW9uKG1ldHJvX2NvdW50aWVzKSANCg0KI25ld19oaWdod2F5IDwtIHN0X3dyaXRlKG5ld19oaWdod2F5LCJDOi9Vc2Vycy9MaW5kc2V5L0Rlc2t0b3AvQ1BMTjY3NV9MYW5kX1VzZV9Nb2RlbGluZ19EZXNrdG9wL0ZpbmFsL0xVRU1fQTVfUmVwby9EYXRhL2dlb2pzb24vbmV3X2hpZ2h3YXkuZ2VvanNvbiIpDQoNCiNBTEwgSElHSFdBWVMgTUVSR0VEIFRPR0VUSEVSIC0tIGhpZ2h3YXlzX2FsbCAtLSAgDQoNCmhpZ2h3YXlzX2FsbCA8LSBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYy10b3duc2xleS9MVUVNX0E1X1JlcG8vYTVhNWIwYzkwOTliOTdmNjhjMGQxYWVhZGNkZmU5ZGJkMjk2MGE5YS9EYXRhL2dlb2pzb24vaGlnaHdheXNfYWxsLmdlb2pzb24iKSAlPiUgDQpzdF90cmFuc2Zvcm0oc3RfY3JzKG1ldHJvX2NvdW50aWVzKSkgJT4lDQogIHN0X2ludGVyc2VjdGlvbihtZXRyb19jb3VudGllcykNCmBgYA0KDQpgYGB7ciBtYXBwaW5nIHRoZSBuZXcgaGlnaHdheSBhZGRpdGlvbiwgZmlnLndpZHRoPTh9DQoNCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPWRldl9jaGFuZ2VfZmlzaG5ldCwgDQogICAgICAgICAgICAgYWVzKHg9eHlDKGRldl9jaGFuZ2VfZmlzaG5ldCkkeCwgeT14eUMoZGV2X2NoYW5nZV9maXNobmV0KSR5LCBjb2xvdXI9ZGV2ZWxvcG1lbnRfY2hhbmdlKSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgIiNFRDc5NTNGRiIpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyIERldmVsb3BtZW50IENoYW5nZSIsDQogICAgICAgc3VidGl0bGUgPSAiQXRsYW50YSBtZXRybyBhcmVhIHwgRmlzaG5ldCBjZW50cm9pZHMgd2l0aCBleGlzdGluZyBoaWdod2F5c1xuaW4gZGFyayBibHVlIGFuZCBhIG5ldyBoaWdod2F5IGluIGN5YW4iKSArDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywgZmlsbD1OQSwgY29sb3VyPSJ3aGl0ZSIsIHNpemU9Ljc1KSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBnZW9tX3NmKGRhdGE9QXRsYW50YUhpZ2h3YXlzLCBjb2xvciA9IGhpZ2hsaWdodCwgbGluZXdpZHRoID0gLjUpICsNCiAgZ2VvbV9zZihkYXRhPW5ld19oaWdod2F5LCBjb2xvdXI9ImN5YW4iLCBsaW5ld2lkdGg9MSkgKw0KICBtYXBUaGVtZQ0KDQpgYGANCg0KTm93LCB3aXRoIHRoZSBuZXcgaGlnaHdheXMgZGF0YXNldCwgd2UgY2FuIGNyZWF0ZSBhIG5ldyBkaXN0YW5jZS10by1oaWdod2F5IHJhc3RlciwgYW5kIGZyb20gdGhlcmUgY29udmVydCBpdCB0byBmaXNobmV0IGNlbnRyb2lkcyB3aGljaCB3ZSBtZXJnZSB3aXRoIHRoZSBmaXNobmV0LiBUaGlzIGhlbHBzIHVzIHVuZGVyc3RhbmQgaG93IGZhciB0aGUgZmlzaG5ldCBjZWxscyBhcmUgZnJvbSB0aGUgbmV3IGhpZ2h3YXkuDQoNCmBgYHtyIGNyZWF0ZSBoaWdod2F5IGRpc3RhbmNlIHJhc3RlciB3aXRoIG5ldyBoaWdod2F5IHNlZ21lbnQgfQ0KDQojbmV3X2hpZ2h3YXlfcmFzdGVyIDwtIA0KICMgIGFzKGhpZ2h3YXlzX2FsbCwnU3BhdGlhbCcpICU+JQ0KICAjIHJhc3Rlcml6ZSguLGVtcHR5UmFzdGVyKQ0KDQojbmV3X2hpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlIDwtIGRpc3RhbmNlKG5ld19oaWdod2F5X3Jhc3RlcikNCiN3cml0ZVJhc3RlcihuZXdfaGlnaHdheV9yYXN0ZXJfZGlzdGFuY2UsIGZpbGVuYW1lPWZpbGUucGF0aChyYXN0X2RpciwgIm5ld19oaWdod2F5X2Rpc3QudGlmIiksIGZvcm1hdD0iR1RpZmYiLCBvdmVyd3JpdGU9VFJVRSkNCg0KbmV3X2hpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlIDwtIHJhc3RlcihwYXN0ZShkaXJfZ2l0LCAiMTg3YWM3Y2YyMzEyNTdmYzUyZGUwZjdmMmExZGMyMDE0ZTRlNmYyZC9EYXRhL3Jhc3Rlci9uZXdfaGlnaHdheV9kaXN0LnRpZiIsIHNlcD0nLycpKQ0KDQpuYW1lcyhuZXdfaGlnaHdheV9yYXN0ZXJfZGlzdGFuY2UpIDwtICJkaXN0YW5jZV9oaWdod2F5cyINCg0KbmV3X2hpZ2h3YXlQb2ludHMgPC0NCiAgcmFzdGVyVG9Qb2ludHMobmV3X2hpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKEF0bGFudGFNU0FfZmlzaG5ldCkpDQoNCm5ld19oaWdod2F5UG9pbnRzX2Zpc2huZXQgPC0gDQogIGFnZ3JlZ2F0ZShuZXdfaGlnaHdheVBvaW50cywgQXRsYW50YU1TQV9maXNobmV0LCBtZWFuKSAlPiUNCiAgbXV0YXRlKGRpc3RhbmNlX2hpZ2h3YXlzID0gaWZlbHNlKGlzLm5hKGRpc3RhbmNlX2hpZ2h3YXlzKSwwLGRpc3RhbmNlX2hpZ2h3YXlzKSkNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMpICsNCiAgZ2VvbV9wb2ludChkYXRhPW5ld19oaWdod2F5UG9pbnRzX2Zpc2huZXQsIGFlcyh4PXh5QyhuZXdfaGlnaHdheVBvaW50c19maXNobmV0KVssMV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT14eUMobmV3X2hpZ2h3YXlQb2ludHNfZmlzaG5ldClbLDJdLCANCiAgICAgICAgICAgICAgICAgY29sb3VyPWZhY3RvcihudGlsZShkaXN0YW5jZV9oaWdod2F5cyw1KSkpLHNpemU9MS41KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhuZXdfaGlnaHdheVBvaW50c19maXNobmV0LCJkaXN0YW5jZV9oaWdod2F5cyIpLDEsOCksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhPWhpZ2h3YXlzX2FsbCwgY29sb3VyID0gImJsYWNrIikgKw0KICBnZW9tX3NmKGRhdGE9bmV3X2hpZ2h3YXksIGNvbG91cj0iY3lhbiIsIGxpbmV3aWR0aD0uNzUpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzLCBmaWxsPU5BLCBjb2xvdXI9IndoaXRlIiwgc2l6ZT0uNzUpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgdG8gSGlnaHdheXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzOyBFeGlzdGluZyBoaWdod2F5cyB2aXN1YWxpemVkIGluIGJsYWNrOyBuZXcgaGlnaHdheSBpbiBjeWFuIikgKw0KICBtYXBUaGVtZQ0KDQpgYGANCg0KV2UgdGhlbiB1c2UgTW9kZWwgNiB0byBwcmVkaWN0IGhvdyBhZGRpbmcgYSBoaWdod2F5IG1pZ2h0IGFmZmVjdCBkZXZlbG9wbWVudCBwYXR0ZXJucyBpbiAyMDI5LCBiYXNlZCBzY2VuYXJpbyAxLiBXaGlsZSB0aGlzIG1vZGVsIGRvZXMgbm90IHNob3cgYSB0cnVlIHN1cHBseS1zaWRlIG9ubHkgZWZmZWN0LCBpdCBpcyB1c2VmdWwgYmVjYXVzZSBpdCBhbGxvd3MgdXMgdG8gZGlyZWN0bHkgY29tcGFyZSBob3cgYWRkaW5nIGluIGEgc3VwcGx5LXNpZGUgZmFjdG9yIGFmZmVjdHMgdGhlIG1vZGVsJ3MgZGVtYW5kLXNpZGUgb25seSBwcmVkaWN0aW9ucy4NCg0KYGBge3IgcHJlZGljdCB3aXRoIG5ldyBoaWdod2F5LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9DQpkYXRfaGlnaHdheSA8LQ0KICBkYXRfaW5maWxsICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1kaXN0YW5jZV9oaWdod2F5cykgJT4lIA0KICBsZWZ0X2pvaW4oYXMuZGF0YS5mcmFtZShuZXdfaGlnaHdheVBvaW50c19maXNobmV0KSkgJT4lIA0KICBtdXRhdGUocHJlZGljdF8yMDI5LmRpc3RhbmNlX2hpZ2h3YXlzID0gcHJlZGljdChNb2RlbDYsLiAsIHR5cGU9InJlc3BvbnNlIikpDQoNCmdyaWQuYXJyYW5nZShuY29sPTIsDQogICAgICAgICAgICAgDQogICAgICAgICAgICAgbWFwX3ByZWQyOSwNCg0KZGF0X2hpZ2h3YXkgJT4lDQogIGdncGxvdCgpICsgIA0KICBnZW9tX3BvaW50KGFlcyh4PXh5QyhkYXRfaGlnaHdheSlbLDFdLCB5PXh5QyhkYXRfaGlnaHdheSlbLDJdLCBjb2xvdXIgPSBmYWN0b3IobnRpbGUocHJlZGljdF8yMDI5LmRpc3RhbmNlX2hpZ2h3YXlzLDUpKSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhkYXRfaGlnaHdheSwicHJlZGljdF8yMDI5LmRpc3RhbmNlX2hpZ2h3YXlzIiksMSw0KSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iJSBMaWtlbGlob29kXG5vZiBEZXZlbG9wbWVudCIpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzLCBmaWxsPU5BLCBjb2xvdXI9IndoaXRlIiwgc2l6ZT0uNzUpICsNCiAgZ2VvbV9zZihkYXRhPWhpZ2h3YXlzX2FsbCwgY29sb3VyID0gImJsYWNrIikgKw0KICBnZW9tX3NmKGRhdGE9bmV3X2hpZ2h3YXksIGNvbG91cj0iY3lhbiIsIGxpbmV3aWR0aD0uNzUpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGU9ICJEZXZlbG9wbWVudCBTdXBwbHkgaW4gMjAyOTogUHJlZGljdGVkIFByb2JhYmlsaXRpZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkJhc2VkIG9uIHByb2plY3RlZCBkZXZlbG9wbWVudCByZXNwb25zZSB0byBhIG5ldyBoaWdod2F5IikgKw0KICBtYXBUaGVtZQ0KKQ0KYGBgDQoNCldoaWxlIHRoZSBjaGFuZ2UgaXMgc2xpZ2h0LCB3ZSBzZWUgdGhhdCBhZGRpbmcgaW4gdGhlIG5ldyBoaWdod2F5IGRvZXMgc2xpZ2h0bHkgaW5jcmVhc2UgdGhlIHByZWRpY3RlZCBsaWtlbGlob29kIG9mIGRldmVsb3BtZW50IGluIHRoZSBhcmVhIGFyb3VuZCBpdC4NCg0KIyBBbGxvY2F0aW9uDQoNCk5vdyB0aGF0IHdlIGJldHRlciB1bmRlcnN0YW5kIHRoZSBlZmZlY3RzIG9mIGRlbWFuZC1zaWRlIGZhY3RvcnMgKGxpa2UgcG9wdWxhdGlvbiBncm93dGggYW5kIHByb3hpbWl0eSB0byBkZXZlbG9wZWQgYXJlYXMpIGFuZCBzdXBwbHktc2lkZSBmYWN0b3JzIChsaWtlIG5ldyBpbmZyYXN0cnVjdHVyZSBhdmFpbGFibGUgbGFuZCksIHdlIGNhbiBhbmFseXplIHRoZXNlIGZhY3RvcnMgYnkgY291bnR5LiBUaGlzIGFsbG93cyB1cyB0byBiZXR0ZXIgdW5kZXJzdGFuZCB3aGljaCBjb3VudGllcyBpbiB0aGUgQXRsYW50YSBNU0EgYXJlIGJldHRlciBzdWl0ZWQgdG8gZGV2ZWxvcG1lbnQgdGhhbiBvdGhlcnMuIEl0IGFsc28gYWxsb3dzIHVzIHRvIHNlZSB3aGljaCBsYW5kIHVzZSBhbmQgZGV2ZWxvcG1lbnQgcG9saWNpZXMgbWlnaHQgYmUgYmVzdCBzdWl0ZWQgdG8gdGhpcyByZWdpb24uDQoNCkxldCdzIGZpcnN0IG9yaWVudCBvdXJzZWx2ZXMgdG8gaG93IGVhY2gga2luZCBvZiBsYW5kIGNvdmVyIHdhcyBkaXN0cmlidXRlZCBpbiAyMDE5Lg0KDQpgYGB7ciBhZ2dyZWdhdGUgMjAxOSByYXN0ZXJzLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQpkYXQyIDwtDQogIGFnZ3JlZ2F0ZVJhc3Rlcih0aGVSYXN0ZXJMaXN0MTksIGRhdCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcGVkMTksIGZvcmVzdDE5LCBmYXJtMTksIHdldGxhbmRzMTksIG90aGVyVW5kZXZlbG9wZWQxOSwgd2F0ZXIxOSkgJT4lDQogIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUNCiAgYmluZF9jb2xzKC4sZGF0KSAlPiUNCiAgc3Rfc2YoKSAlPiUNCiAgc3RfY2FzdCgiUE9MWUdPTiIpDQoNCnJhc3RlcnMxOV9tYXAgPC0gYWdncmVnYXRlZFJhc3RlcnMxOSAlPiUNCiAgZ2F0aGVyKHZhcix2YWx1ZSxkZXZlbG9wZWQxOTp3YXRlcjE5KSAlPiUNCiAgc3RfY2FzdCgiUE9MWUdPTiIpICU+JSAgICAjanVzdCB0byBtYWtlIHN1cmUgbm8gd2VpcmQgZ2VvbWV0cmllcyBzbGlwcGVkIGluDQogIG11dGF0ZShYID0geHlDKC4pJHgsDQogICAgICAgICBZID0geHlDKC4pJHkpICU+JQ0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzKSArDQogICAgZ2VvbV9wb2ludChhZXMoWCxZLCBjb2xvdXI9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAgICBmYWNldF93cmFwKH52YXIpICsNCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgaGlnaGxpZ2h0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJGYWxzZSIsIlRydWUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKw0KICAgIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjI1KSArDQogICAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNSkgKw0KICAgIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBUeXBlcywgMjAxOSIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgIG1hcFRoZW1lICsNCiAgIHRoZW1lKCkgICsNCiAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KcmFzdGVyczE5X21hcA0KYGBgDQoNCldlIHNlZSB0aGF0IG11Y2ggb2YgdGhlIG1ldHJvIGFyZWEsIGluY2x1ZGluZyBkZXZlbG9wZWQgYXJlYXMsIGlzIGZvcmVzdGVkIC0gbWVhbmluZyBpdCBoYXMgYSBsYXJnZSBhbW91bnQgb2YgdHJlZSBjb3Zlci4gRmFybXMgYW5kIG90aGVyIHVuZGV2ZWxvcGVkIGxhbmRzIHRlbmQgdG8gYmUgbG9jYXRlZCBtb3JlIG9uIHRoZSBvdXRlciBlZGdlcy4gV2V0bGFuZHMgb24gdGhlIG90aGVyIGhhbmQgYXJlIGNvbmNlbnRyYXRlZCBtb3N0bHkgaW4gdGhlIG1ldHJvIHJlZ2lvbidzIHNvdXRoZXJuIGNvdW50aWVzLCBhbG9uZyB3aXRoIGEgbnVtYmVyIG9mIHNtYWxsIHBvbmRzIGFuZCByaXZlcnMuIFRoZSBsYXJnZXN0IHdhdGVyIGJvZGllcywgaG93ZXZlciwgYXJlIGxvY2F0ZWQgaW4gdGhlIG5vcnRoIG9mIHRoZSBtZXRyby4NCg0KTWFwcGluZyB0aGVzZSBsYW5kIGNvdmVyIHR5cGVzIHNlcGFyYXRlbHkgYWxsb3dzIHVzIHRvIHNlZSB0aGF0IHNvbWUgY291bnRpZXMgbWF5IGJlIGJldHRlciBzdWl0ZWQgdG8gZGV2ZWxvcG1lbnQsIHdoaWxlIG90aGVycyBoYXZlIG1vcmUgc2Vuc2l0aXZlIGxhbmQgY292ZXIgdGhhdCBzaG91bGQgYmUgcHJvdGVjdGVkLg0KDQpIb3dldmVyLCB0aGlzIG1ldGhvZCBkb2VzIG5vdCBhbGxvdyB1cyB0byBwaW5wb2ludCB3aGVyZSB0aGVzZSBsb2NhdGlvbnMgYXJlIHdpdGggbXVjaCBzcGVjaWZpY2l0eS4NCg0KIyMgU2Vuc2l0aXZlIExhbmQgQ292ZXIgTG9zdA0KDQpUbyBiZXR0ZXIgdW5kZXJzdGFuZCB3aGVyZSBlY29sb2dpY2FsbHkgaW1wb3J0YW50IGxhbmRzIGFyZSBiZWluZyBsb3N0IGZvciBkZXZlbG9wbWVudCwgd2UgY3JlYXRlIGFuIGluZGljYXRvciBsYXllciBjYWxsZWQgYHNlbnNpdGl2ZV9sb3N0MTlgLiBUaGlzIG5ldyBsYXllciBzaG93cyB3aGVyZSBmb3Jlc3Qgb3Igd2V0bGFuZHMgd2VyZSBsb3N0IHRvIGRldmVsb3BtZW50IGJldHdlZW4gMjAwOSBhbmQgMjAxOS4NCg0KYGBge3Igc2Vuc2l0aXZlIGxhbmRjb3ZlciBsb3NzLCBmaWcud2lkdGg9OH0NCmRhdDIgPC0NCiAgZGF0MiAlPiUNCiAgIG11dGF0ZShzZW5zaXRpdmVfbG9zdDE5ID0gaWZlbHNlKGZvcmVzdDA4ID09IDEgJiBmb3Jlc3QxOSA9PSAwIHwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdldGxhbmRzMDggPT0gMSAmIHdldGxhbmRzMTkgPT0gMCwxLDApKSAlPiUgDQogIHN0X3RyYW5zZm9ybSgiRVNSSToxMDIyNjciKQ0KICAgICAgICAgICAgICAgICAgICAgIA0KZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGRhdGE9ZGF0MiwgYWVzKHg9eHlDKGRhdDIpWywxXSwgeT14eUMoZGF0MilbLDJdLCBjb2xvdXI9YXMuZmFjdG9yKHNlbnNpdGl2ZV9sb3N0MTkpKSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgaGlnaGxpZ2h0KSwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiU2Vuc2l0aXZlIExvc3QiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgZ2VvbV9zZihkYXRhID0gbWV0cm9fY291bnRpZXMsIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IC41KSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJTZW5zaXRpdmUgbGFuZHMgbG9zdDogMjAwOSAtIDIwMTkiLA0KICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKw0KICBtYXBUaGVtZQ0KYGBgDQoNCkludGVyZXN0aW5nbHksIHdlIHNlZSB0aGF0IG1vcmUgc2Vuc2l0aXZlIGFyZWFzIHdlcmUgbG9zdCB0byBkZXZlbG9wbWVudCBuZWFyIHRoZSBtZXRybydzIGNlbnRlciBkdXJpbmcgdGhpcyBwZXJpb2QuDQoNCiMjIExhbmRzY2FwZSBGcmFnbWVudGF0aW9uDQoNClRvIGNvbnRleHR1YWxpemUgdGhlc2Ugc2Vuc2l0aXZlIGxvY2F0aW9ucywgd2UgZ3JvdXAgYXJlYXMgd2hlcmUgdGhlIGB3ZXRsYW5kczE5YCBhbmQgYGZvcmVzdDExYCByYXN0ZXJzIGFyZSBjb250aWd1b3VzIHRvIGNyZWF0ZSBhIGBzZW5zaXRpdmVfcmVnaW9uc2AgbGF5ZXIgY29udGFpbmluZyBhbGwgc2Vuc2l0aXZlIHJlZ2lvbnMgd2l0aCBhbiBhcmVhIGdyZWF0ZXIgdGhhbiAxIGFjcmUuDQoNCmBgYHtyIHNlbnNpdGl2ZSByZWdpb25zLCBmaWcud2lkdGg9OH0NCnNlbnNpdGl2ZVJlZ2lvbnMgPC0gDQogIHJhc3Rlcjo6Y2x1bXAod2V0bGFuZHMxOSArIGZvcmVzdDE5KSAlPiUgI3VuaXRzIGFyZSBtZXRlcnMNCiAgcmFzdGVyVG9Qb2x5Z29ucygpICU+JQ0KICBzdF9hc19zZigpICU+JQ0KICBncm91cF9ieShjbHVtcHMpICU+JSANCiAgc3VtbWFyaXplKCkgJT4lDQogICAgbXV0YXRlKEFjcmVzID0gYXMubnVtZXJpYyhzdF9hcmVhKC4pICogMC4wMDAyNDcxMSkpICU+JSAjVGhlcmUgYXJlIDAuMDAwMjQ3MTEgYWNyZXMgcGVyIHNxdWFyZSBtZXRlciBhbmQgNDA0Ni44NiBzcXVhcmUgbWV0ZXJzIGluIGFuIGFjcmUNCiAgICBmaWx0ZXIoQWNyZXMgPiA0MDQ2Ljg2KSAgJT4lDQogIGRwbHlyOjpzZWxlY3QoKSAlPiUNCiAgcmFzdGVyOjpyYXN0ZXJpemUoLixlbXB0eVJhc3RlcikgDQoNCnNlbnNpdGl2ZVJlZ2lvbnNbc2Vuc2l0aXZlUmVnaW9ucyA+IDBdIDwtIDEgIA0KbmFtZXMoc2Vuc2l0aXZlUmVnaW9ucykgPC0gInNlbnNpdGl2ZVJlZ2lvbnMiDQoNCmRhdDIgPC0NCiAgYWdncmVnYXRlUmFzdGVyKGMoc2Vuc2l0aXZlUmVnaW9ucyksIGRhdDIpICU+JQ0KICBkcGx5cjo6c2VsZWN0KHNlbnNpdGl2ZVJlZ2lvbnMpICU+JQ0KICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lDQogIGJpbmRfY29scyguLGRhdDIpICU+JQ0KICBzdF9zZigpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPWRhdDIsIGFlcyh4PXh5QyhkYXQyKVssMV0sIHk9eHlDKGRhdDIpWywyXSwgY29sb3VyPWFzLmZhY3RvcihzZW5zaXRpdmVSZWdpb25zKSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKGdyYXksIGhpZ2hsaWdodCksDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk90aGVyIiwiU2Vuc2l0aXZlIFJlZ2lvbnMiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArDQogIGdlb21fc2YoZGF0YSA9IG1ldHJvX2NvdW50aWVzLCBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAuNSkgKw0KICBnZW9tX3NmKGRhdGEgPSBBdGxhbnRhX01TQSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGUgPSAiU2Vuc2l0aXZlIHJlZ2lvbnMiLA0KICAgICAgIHN1YnRpdGxlID0gIkNvbnRpbm91cyBhcmVhcyBvZiBlaXRoZXIgd2V0bGFuZHMgb3IgZm9yZXN0c1xuZ3JlYXRlciB0aGFuIDEgYWNyZSIpICsNCiAgbWFwVGhlbWUNCmBgYA0KDQpUaGlzIGdpdmVzIHVzIGEgbXVjaCBtb3JlIGNsZWFyIHVuZGVyc3RhbmRpbmcgb2Ygd2hpY2ggYXJlYXMgbWlnaHQgYmUgbW9zdCBzZW5zaXRpdmUgdG8gZGV2ZWxvcG1lbnQgdGhhbiBtYXBwaW5nIGVhY2ggdHlwZSBvZiBsYW5kIGNvdmVyIHNlcGFyYXRlbHkgZGlkLiBXZSBzZWUgdGhhdCBtb3N0IG9mIHRoZSBjb3VudGllcyBhbG9uZyB0aGUgb3V0ZXIgYm9yZGVyIG9mIHRoZSBBdGxhbnRhIG1ldHJvIHJlZ2lvbiBzdGlsbCBoYXZlIHJlbGF0aXZlbHkgbG93IGxhbmRzY2FwZSBmcmFnbWVudGF0aW9uLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlc2UgY291bnRpZXMgc2hvdWxkIHByaW9yaXRpemUgaW5maWxsIGRldmVsb3BtZW50IG92ZXIgc3VidXJiYW4gc3ByYXdsLg0KDQojIyBTdW1tYXJpemUgYnkgQ291bnR5DQoNClRvIGJldHRlciB1bmRlcnN0YW5kIGhvdyB0aGVzZSBjaGFyYWN0ZXJpc3RpY3MgY29tcGFyZSBhY3Jvc3MgY291bnRpZXMsIHdlIGNyZWF0ZSBhIG1hdHJpeCBvZiBkZW1hbmQgc2lkZSBhbmQgc3VpdGFiaWxpdHkgZmFjdG9ycy4NCg0KVGhpcyBtYXRyaXggYWxsb3dzIHVzIHRvIGJldHRlciB1bmRlcnN0YW5kIGVhY2ggY291bnR5J3Mgc3VpdGFiaWxpdHkgZm9yIGRldmVsb3BtZW50Lg0KDQpgYGB7ciBjb3VudHkgc3BlY2lmaWMgbWV0cmljc30NCmNvdW50eV9zcGVjaWZpY19tZXRyaWNzIDwtIA0KICBkYXQyICU+JQ0KICAjcHJlZGljdCBkZXZlbG9wbWVudCBkZW1hbmQgZnJvbSBvdXIgbW9kZWwNCiAgbXV0YXRlKERldmVsb3BtZW50X0RlbWFuZCA9IHByZWRpY3QoTW9kZWw2LCBkYXQyLCB0eXBlPSJyZXNwb25zZSIpKSAlPiUNCiAgI2dldCBhIGNvdW50IGNvdW50IG9mIGdyaWQgY2VsbHMgYnkgY291bnR5IHdoaWNoIHdlIGNhbiB1c2UgdG8gY2FsY3VsYXRlIHJhdGVzIGJlbG93DQogIGxlZnRfam9pbihzdF9zZXRfZ2VvbWV0cnkoZGF0LCBOVUxMKSAlPiUgZ3JvdXBfYnkoTkFNRSkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkpICU+JQ0KICAjY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBieSBjb3VudHkNCiAgZ3JvdXBfYnkoTkFNRSkgJT4lDQogIHN1bW1hcml6ZShUb3RhbF9GYXJtbGFuZCA9IHN1bShmYXJtMTkpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgIFRvdGFsX0ZvcmVzdCA9IHN1bShmb3Jlc3QxOSkgLyBtYXgoY291bnQpLA0KICAgICAgICAgICAgVG90YWxfV2V0bGFuZHMgPSBzdW0od2V0bGFuZHMxOSkgLyBtYXgoY291bnQpLA0KICAgICAgICAgICAgVG90YWxfVW5kZXZlbG9wZWQgPSBzdW0ob3RoZXJVbmRldmVsb3BlZDE5KSAvIG1heChjb3VudCksDQogICAgICAgICAgICBTZW5zaXRpdmVfTGFuZF9Mb3N0ID0gc3VtKHNlbnNpdGl2ZV9sb3N0MTkpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgIFNlbnNpdGl2ZV9SZWdpb25zID0gc3VtKHNlbnNpdGl2ZVJlZ2lvbnMpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgIE1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kID0gbWVhbihEZXZlbG9wbWVudF9EZW1hbmQpKSAlPiUNCiAgI2dldCBwb3B1bGF0aW9uIGRhdGEgYnkgY291bnR5DQogIGxlZnRfam9pbihjb3VudHlQb3B1bGF0aW9uXzIwMjkgJT4lIA0KICAgICAgICAgICAgbXV0YXRlKFBvcHVsYXRpb25fQ2hhbmdlID0gY291bnR5X3Byb2plY3Rpb25fMjAyOSAtIGNvdW50eV9wb3B1bGF0aW9uXzIwMTksDQogICAgICAgICAgICAgICAgICAgUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSA9IFBvcHVsYXRpb25fQ2hhbmdlIC8gY291bnR5X3Byb2plY3Rpb25fMjAyOSkgJT4lDQogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KE5BTUUsUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSkpDQpgYGANCg0KYGBge3IgcGxvdCBjb3VudHkgc3BlY2lmaWMgbWV0cmljcywgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTExfQ0KY291bnR5X3NwZWNpZmljX21ldHJpY3MgJT4lDQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1OQU1FLCAtZ2VvbWV0cnkpICU+JQ0KICBtdXRhdGUoVmFyaWFibGUgPSBmYWN0b3IoVmFyaWFibGUsIGxldmVscz1jKCJQb3B1bGF0aW9uX0NoYW5nZV9SYXRlIiwiTWVhbl9EZXZlbG9wbWVudF9EZW1hbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUb3RhbF9GYXJtbGFuZCIsIlRvdGFsX1VuZGV2ZWxvcGVkIiwiVG90YWxfRm9yZXN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfV2V0bGFuZHMiLCJTZW5zaXRpdmVfTGFuZF9Mb3N0IiwiU2Vuc2l0aXZlX1JlZ2lvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUUlVFKSkpICU+JQ0KICBtdXRhdGUoUGxhbm5pbmdfRGVzaWduYXRpb24gPSBjYXNlX3doZW4oDQogICAgVmFyaWFibGUgPT0gIlBvcHVsYXRpb25fQ2hhbmdlX1JhdGUiIHwgVmFyaWFibGUgPT0gIk1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kIiB+ICJEZW1hbmQtU2lkZSIsDQogICAgVmFyaWFibGUgPT0gIlRvdGFsX0Zhcm1sYW5kIiB8IFZhcmlhYmxlID09ICJUb3RhbF9VbmRldmVsb3BlZCIgICAgICAgICAgICAgICB+ICJTdWl0YWJsZSIsDQogICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+ICJOb3QgU3VpdGFibGUiKSkgJT4lDQogIGdncGxvdChhZXMoeD1WYXJpYWJsZSwgeT1WYWx1ZSwgZmlsbD1QbGFubmluZ19EZXNpZ25hdGlvbikpICsNCiAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCksIGNvbG91cj0iYmxhY2siKSArDQogICAgZmFjZXRfd3JhcCh+TkFNRSwgbmNvbD01KSArDQogICAgY29vcmRfZmxpcCgpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKC4yNSwgMSwgYnkgPSAuMjUpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMi41KSArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQuNSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjMEQwODg3RkYiLCIjQkQzNzg2RkYiLCIjRkRDOTI2RkYiKSkgKw0KICAgIGxhYnModGl0bGU9ICJDb3VudHkgU3BlY2lmaWMgQWxsb2NhdGlvbiBNZXRyaWNzIiwgc3VidGl0bGU9ICJBcyByYXRlcyIsIHg9IkluZGljYXRvciIsIHk9IlJhdGUiKSArDQogICAgcGxvdFRoZW1lICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQ0KYGBgDQoNClRoaXMgY2hhcnQgc2hvd3MgdGhyZWUgdHlwZXMgb2YgbWV0cmljczogIA0KLSAgIERlbWFuZC1zaWRlIGZhY3RvcnMgIA0KLSAgIEZhY3RvcnMgc3VpdGFibGUgZm9yIGRldmVsb3BtZW50LCBhbmQgIA0KLSAgIEZhY3RvcnMgbm90IHN1aXRhYmxlIGZvciBkZXZlbG9wbWVudCAgDQoNClRoZSBkZW1hbmQgc2lkZSBmYWN0b3JzIGFyZSBgTWVhbl9EZXZlbG9wbWVudF9EZW1hbmRgIGFuZCBgUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZWAuIGBNZWFuX0RldmVsb3BtZW50X0RlbWFuZGAgaXMgdGhlIG1lYW4gdmFsdWUgb2YgcHJlZGljdGVkIGRldmVsb3BtZW50IGRlbWFuZCBmb3IgYSBnaXZlbiBjb3VudHkgaW4gMjAyOS4gYFBvcHVsYXRpb25fQ2hhbmdlX1JhdGVgIGlzIHRoZSBzaGFyZSBvZiBhIGNvdW50eSdzIHBvcHVsYXRpb24gdGhhdCBpcyBwcm9qZWN0ZWQgdG8gbW92ZS1pbiBiZXR3ZWVuIDIwMTkgYW5kIDIwMjkuDQoNCkZhY3RvcnMgc3VpdGFibGUgZm9yIGRldmVsb3BtZW50IGFyZSBhcmVhIG9mIGBUb3RhbF9GYXJtbGFuZGAgYW5kIGFyZWEgb2YgYFRvdGFsX1VuZGV2ZWxvcGVkYCBsYW5kIGluIGEgZ2l2ZW4gY291bnR5Lg0KDQpGYWN0b3JzIG5vdCBzdWl0YWJsZSBmb3IgZGV2ZWxvcG1lbnQgYXJlIGFyZWEgb2YgYFNlbnNpdGl2ZV9SZWdpb25zYCwgYFNlbnNpdGl2ZV9MYW5kX0xvc3RgLCBhbmQgYFRvdGFsX1dldGxhbmRzYCBpbiBhIGNvdW50eS4NCg0KSW4gZGV0ZXJtaW5pbmcgb3VyIGFsbG9jYXRpb24gY3JpdGVyaWEsIHdlIGRldGVybWluZWQgYW55IGFyZWEgd2l0aCBhIHdldGxhbmQgd2FzIG5vdCBzdWl0YWJsZSBmb3IgZGV2ZWxvcG1lbnQuIEF0dGVuZGluZyB0byB0aGUgZXh0ZXJuYWxpdGllcyBvZiB1cmJhbiBzcHJhd2wsIHdlIGNob3NlIHRvIHByZXNlcnZlIHRoZXNlIHZpdGFsIGVjb3N5c3RlbXMuIFdoZW4gaXQgY29tZXMgdG8gZm9yZXN0cywgaG93ZXZlciwgZ2l2ZW4gdGhhdCB0aGUgQXRsYW50YSBNU0EgaGFzIHN1Y2ggZXh0ZW5zaXZlIHRyZWUgY292ZXIsIHdlIGRldGVybWluZWQgdGhhdCBmb3Jlc3RlZCBhcmVhcyBhcmUgc3VpdGFibGUgZm9yIGRldmVsb3BtZW50Lg0KDQpMb29raW5nIGF0IHRoZSBkZXZlbG9wbWVudCBzdWl0YWJpbGl0eSBpbmRpY2F0b3JzIGFjcm9zcyB0aGUgMjEgY291bnRpZXMgb2YgR2VvcmdpYSdzIE1TQSwgd2Ugc2VlIHRoYXQgYWxsIGNvdW50aWVzIGhhdmUgYSBsYXJnZSBwZXJjZW50YWdlIG9mIGZvcmVzdGVkIGxhbmQgYW5kIHRoZXJlZm9yZSBvZiBzZW5zaXRpdmUgcmVnaW9ucy4gTG9va2luZyBhdCB0aGUgcmVzdCBvZiB0aGUgaW5kaWNhdG9ycyB0b2dldGhlciBob3dldmVyLCB3ZSBjYW4gc2VlIHRoYXQgc29tZSBjb3VudGllcyBhcmUgYmV0dGVyIHN1aXRlZCBmb3IgZGV2ZWxvcG1lbnQgdGhhbiBvdGhlcnMuDQoNCkZvciBleGFtcGxlLCB3ZSBzZWUgdGhhdCBIYWxsIENvdW50eSBoYXMgYm90aCBhIGhpZ2ggcmF0ZSBvZiBkZXZlbG9wbWVudCBkZW1hbmQgYW5kIG9mIHBvcHVsYXRpb24gY2hhbmdlLiBJdCBhbHNvIGhhcyBhIHJlbGF0aXZlbHkgbGFyZ2UgYW1vdW50IG9mIGZhcm1sYW5kIGFuZCBhIHJlbGF0aXZlbHkgbG93IGFtb3VudCBvZiB0b3RhbCB3ZXRsYW5kcyBhbmQgc2Vuc2l0aXZlIGxhbmQgbG9zdC4gVG9nZXRoZXIsIHRoZXNlIGZhY3RvcnMgc3VnZ2VzdCB0aGF0IEhhbGwgQ291bnR5IGhhcyBhbXBsZSBzcGFjZSBmb3IgZGV2ZWxvcG1lbnQgdGhhdCB3aWxsIG5vdCBpbXBhY3Qgc2Vuc2l0aXZlIGFyZWFzIChvdGhlciB0aGFuIGZvcmVzdCkuDQoNClRoZSBzYXRlbGxpdGUgaW1hZ2UgYmVsb3cgb2YgZGV2ZWxvcG1lbnQgaW4gSGFsbCBDb3VudHkgc2hvd3Mgd2h5IHRoZXJlIGlzIGEgbGFyZ2UgYW1vdW50IG9mIGZvcmVzdGVkIGxhbmQgY292ZXIgYWNyb3NzIHRoZSBBdGxhbnRhIG1ldHJvIHJlZ2lvbi4gVGhlcmUgc2VlbXMgdG8gYmUgYSByZWdpb25hbCBwcmVmZXJlbmNlIGZvciBkaXNwZXJzZWQgZGV2ZWxvcG1lbnQgaW4gZm9yZXN0ZWQgbGFuZC4NCg0KIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby9mZmEwODNlN2UxYTYzYjExZTAzM2YzODZlYzdmZTAwYjJkMzQxNGQzL0RhdGEvSW1hZ2VzL1NhdGVsbGl0ZV9IYWxsQ291bnR5LnBuZykNCg0KDQoNCk9uIHRoZSBvdGhlciBoYW5kLCBDbGF5dG9uIENvdW50eSBhcHBlYXJzIHRvIGJlIHBvb3JseSBzdWl0ZWQgZm9yIGRldmVsb3BtZW50IGJlY2F1c2UgaXQgaGFzIHJlbGF0aXZlbHkgaGlnaCBkZXZlbG9wbWVudCBkZW1hbmQsIGlzIGV4cGVyaWVuY2luZyBwb3B1bGF0aW9uIGdyb3d0aCwgaGFzIGEgbG93IGFtb3VudCBvZiBmYXJtbGFuZCBhbmQgdW5kZXZlbG9wZWQgbGFuZCwgYW5kIGhhcyBhIHJlbGF0aXZlbHkgaGlnaCBhbW91bnQgb2Ygd2V0bGFuZHMuDQoNCkxldCdzIGxvb2sgbW9yZSBjbG9zZWx5IGF0IEhhbGwgQ291bnR5IChpbiB0aGUgTm9ydGgpIGFuZCBDbGF5dG9uIENvdW50eSAodG8gdGhlIFNvdXRoKSB0byBzZWUgd2hhdCB0aGVzZSB0cmFpdHMgbG9vayBsaWtlIHNwYXRpYWxseS4NCg0KYGBge3IgSGFsbCBhbmQgQ2xheXRvbiBDb3VudGllc30NCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gbWV0cm9fY291bnRpZXMsIGZpbGwgPSBncmF5LCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IC41KSArDQogIGdlb21fc2YoZGF0YSA9IGZpbHRlcihtZXRyb19jb3VudGllcywgTkFNRT09IkhhbGwiIHwgTkFNRT09IkNsYXl0b24iKSwgZmlsbCA9ICIjRkRDOTI2RkYiLCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IC41KSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJBdGxhbnRhIE1TQSIsDQogICAgICAgc3VidGl0bGUgPSAiSGFsbCBhbmQgQ2xheXRvbiBDb3VudGllcyBIaWdobGlnaHRlZCIpICsNCiAgbWFwVGhlbWUNCmBgYA0KDQojIyBIYWxsIENvdW50eQ0KDQpGaXJzdCwgd2UgbWFwIHN1cHBseSBhbmQgZGVtYW5kIGZhY3RvcnMgZm9yIEhhbGwgQ291bnR5LCB0byBleGFtaW5lIGFuIGV4YW1wbGUgb2YgYSBjb3VudHkgd2VsbCBzdWl0ZWQgdG8gZnVydGhlciBkZXZlbG9wbWVudC4NCg0KYGBge3IgaGFsbCBjb3VudHksIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KSGFsbENvdW50eSA8LQ0KICBkYXQyICU+JQ0KICAgIG11dGF0ZShEZXZlbG9wbWVudF9EZW1hbmQgPSBwcmVkaWN0KE1vZGVsNiwgZGF0MiwgdHlwZT0icmVzcG9uc2UiKSkgJT4lDQogICAgZmlsdGVyKE5BTUUgPT0gIkhhbGwiKSAlPiUNCiAgICByZW5hbWUoImRldmVsb3BlZDE5IiA9IGRldmVsb3BlZDE5Li4uMikNCg0KSGFsbENvdW50eV9sYW5kVXNlIDwtIHJiaW5kKA0KICBmaWx0ZXIoSGFsbENvdW50eSwgd2V0bGFuZHMxOSA9PSAxICkgJT4lDQogIGRwbHlyOjpzZWxlY3QoKSAlPiUgbXV0YXRlKExhbmRfVXNlID0gIk5vdCBTdWl0YWJsZSIpLA0KICBmaWx0ZXIoSGFsbENvdW50eSwgZGV2ZWxvcGVkMTkgPT0gMSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoKSAlPiUgbXV0YXRlKExhbmRfVXNlID0gIkRldmVsb3BlZCIpKQ0KDQpncmlkLmFycmFuZ2UoDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT1IYWxsQ291bnR5LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUoRGV2ZWxvcG1lbnRfRGVtYW5kLDUpKSksIGNvbG91cj1OQSkgKw0KICBnZW9tX3BvaW50KGRhdGE9SGFsbENvdW50eV9sYW5kVXNlLCBhZXMoeD14eUMoSGFsbENvdW50eV9sYW5kVXNlKVssMV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKEhhbGxDb3VudHlfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gMTUsIHNpemUgPSAzKSArDQogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oQXRsYW50YUhpZ2h3YXlzLGZpbHRlcihtZXRyb19jb3VudGllcywgTkFNRT09IkhhbGwiKSksIGxpbmV3aWR0aD0xLCBjb2xvciA9IGRhcmtHcmF5KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LCBuYW1lPSJEZXZlbG9wbWVudFxuRGVtYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhIYWxsQ291bnR5LCJEZXZlbG9wbWVudF9EZW1hbmQiKSwxLDUpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhncmF5LCIjRjBGOTIxRkYiKSkgKyANCiAgZ2VvbV9zZihkYXRhID0gZmlsdGVyKG1ldHJvX2NvdW50aWVzLCBOQU1FPT0iSGFsbCIpLCBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAxKSArDQogIGxhYnModGl0bGUgPSAiRGV2ZWxvcG1lbnQgUG90ZW50aWFsLCAyMDI5OiBIYWxsIENvdW50eSIsDQogICAgICAgc3VidGl0bGUgPSAiV2l0aCBEZXZlbG9wZWQgYW5kIFVuc3VpdGFibGUgQXJlYXMgZnJvbSAyMDE5IikgKw0KICBtYXBUaGVtZSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9SGFsbENvdW50eSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF9DaGFuZ2UsNSkpKSwgY29sb3VyPU5BKSArDQogIGdlb21fcG9pbnQoZGF0YT1IYWxsQ291bnR5X2xhbmRVc2UsIGFlcyh4PXh5QyhIYWxsQ291bnR5X2xhbmRVc2UpWywxXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT14eUMoSGFsbENvdW50eV9sYW5kVXNlKVssMl0sIGNvbG91cj1MYW5kX1VzZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNSwgc2l6ZSA9IDMpICsNCiAgZ2VvbV9zZihkYXRhPXN0X2ludGVyc2VjdGlvbihBdGxhbnRhSGlnaHdheXMsIGZpbHRlcihtZXRyb19jb3VudGllcywgTkFNRT09IkhhbGwiKSksIGxpbmV3aWR0aD0xLCBjb2xvciA9IGRhcmtHcmF5KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LCBuYW1lPSJQb3B1bGF0aW9uXG5DaGFuZ2UiLA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKEhhbGxDb3VudHksInBvcF9DaGFuZ2UiKSwxLDUpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhncmF5LCIjRjBGOTIxRkYiKSkgKyANCiAgZ2VvbV9zZihkYXRhID0gZmlsdGVyKG1ldHJvX2NvdW50aWVzLCBOQU1FPT0iSGFsbCIpLCBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAxKSArDQogIGxhYnModGl0bGUgPSAiUHJvamVjdGVkIFBvcHVsYXRpb24sIDIwMjk6IEhhbGwgQ291bnR5IiwNCiAgICAgICBzdWJ0aXRsZSA9ICJXaXRoIERldmVsb3BlZCBhbmQgVW5zdWl0YWJsZSBBcmVhcyBmcm9tIDIwMTkiKSArDQogIG1hcFRoZW1lICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwgY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpLCBuY29sPTIpDQpgYGANCg0KTWFwcGluZyBIYWxsIENvdW50eSdzIGRldmVsb3BtZW50IHBvdGVudGlhbCBhbmQgcHJvamVjdGVkIHBvcHVsYXRpb24gd2l0aCBkZXZlbG9wZWQgbGFuZCBhbmQgd2V0bGFuZHMgKHRoZSAibm90IHN1aXRhYmxlIiBsYW5kKSBzaG93cyB1cyB0aGF0IHRoZSBDb3VudHkgc3RpbGwgaGFzIGEgc2lnbmlmaWNhbnQgYW1vdW50IG9mIGFyZWEgdGhhdCBpcyBzdWl0YWJsZSBmb3IgZGV2ZWxvcG1lbnQuIEluIHBhcnRpY3VsYXIsIHRoaXMgY291bnR5IHdvdWxkIGRvIHdlbGwgdG8gcHJpb3JpdGl6ZSBpbmZpbGwgZGV2ZWxvcG1lbnQgYmV0d2VlbiBoaWdod2F5cyBpbiBpdHMgbm9ydGh3ZXN0IGFuZCBzb3V0aGVybiBwb3J0aW9ucyB3aGVyZSB0aGVyZSBpcyBwcmVkaWN0ZWQgdG8gYmUgYm90aCBoaWdoIGRldmVsb3BtZW50IGRlbWFuZCBhbmQgc2lnbmlmaWNhbnQgcG9wdWxhdGlvbiBncm93dGguIFRoaXMgd291bGQgYWxzbyBoZWxwIHRvIHJlZHVjZSBmb3Jlc3QgZnJhZ21lbnRhdGlvbiBhbmQgcHJlc3N1cmUgb24gd2V0bGFuZHMgaW4gdGhlIGNvdW50eSdzIGVhc3QgYW5kIG5vcnRoZWFzdC4NCg0KIyMgQ2xheXRvbiBDb3VudHkNCg0KTmV4dCwgd2UgbWFwIHRoZSBzYW1lIHN1cHBseSBhbmQgZGVtYW5kIGZhY3RvcnMgZm9yIENsYXl0b24gQ291bnR5IHRvIGV4YW1pbmUgYW4gZXhhbXBsZSBvZiBhIGNvdW50eSBub3Qgd2VsbCBzdWl0ZWQgdG8gZnVydGhlciBkZXZlbG9wbWVudC4NCg0KYGBge3IgQ2xheXRvbiBDb3VudHksIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCkNsYXl0b25Db3VudHkgPC0NCiAgZGF0MiAlPiUNCiAgICBtdXRhdGUoRGV2ZWxvcG1lbnRfRGVtYW5kID0gcHJlZGljdChNb2RlbDYsIGRhdDIsIHR5cGU9InJlc3BvbnNlIikpICU+JQ0KICAgIGZpbHRlcihOQU1FID09ICJDbGF5dG9uIikgJT4lDQogICAgcmVuYW1lKCJkZXZlbG9wZWQxOSIgPSBkZXZlbG9wZWQxOS4uLjIpDQoNCkNsYXl0b25Db3VudHlfbGFuZFVzZSA8LSByYmluZCgNCiAgZmlsdGVyKENsYXl0b25Db3VudHksIHdldGxhbmRzMTkgPT0gMSApICU+JQ0KICBkcGx5cjo6c2VsZWN0KCkgJT4lIG11dGF0ZShMYW5kX1VzZSA9ICJOb3QgU3VpdGFibGUiKSwNCiAgZmlsdGVyKENsYXl0b25Db3VudHksIGRldmVsb3BlZDE5ID09IDEpICU+JQ0KICBkcGx5cjo6c2VsZWN0KCkgJT4lIG11dGF0ZShMYW5kX1VzZSA9ICJEZXZlbG9wZWQiKSkNCg0KZ3JpZC5hcnJhbmdlKA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9Q2xheXRvbkNvdW50eSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKERldmVsb3BtZW50X0RlbWFuZCw1KSkpLCBjb2xvdXI9TkEpICsNCiAgZ2VvbV9wb2ludChkYXRhPUNsYXl0b25Db3VudHlfbGFuZFVzZSwgYWVzKHg9eHlDKENsYXl0b25Db3VudHlfbGFuZFVzZSlbLDFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhDbGF5dG9uQ291bnR5X2xhbmRVc2UpWywyXSwgY29sb3VyPUxhbmRfVXNlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE1LCBzaXplID0gNikgKw0KICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKEF0bGFudGFIaWdod2F5cyxmaWx0ZXIobWV0cm9fY291bnRpZXMsIE5BTUU9PSJDbGF5dG9uIikpLCBsaW5ld2lkdGg9MSwgY29sb3IgPSBkYXJrR3JheSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iRGV2ZWxvcG1lbnRcbkRlbWFuZCIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoQ2xheXRvbkNvdW50eSwiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwiI0YwRjkyMUZGIikpICsgDQogIGdlb21fc2YoZGF0YSA9IGZpbHRlcihtZXRyb19jb3VudGllcywgTkFNRT09IkNsYXl0b24iKSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IFBvdGVudGlhbCwgMjAyOTogQ2xheXRvbiBDb3VudHkiLA0KICAgICAgIHN1YnRpdGxlID0gIldpdGggRGV2ZWxvcGVkIGFuZCBVbnN1aXRhYmxlIEFyZWFzIGZyb20gMjAxOSIpICsgDQogIG1hcFRoZW1lICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwgY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpLA0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT1DbGF5dG9uQ291bnR5LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wX0NoYW5nZSw1KSkpLCBjb2xvdXI9TkEpICsNCiAgZ2VvbV9wb2ludChkYXRhPUNsYXl0b25Db3VudHlfbGFuZFVzZSwgYWVzKHg9eHlDKENsYXl0b25Db3VudHlfbGFuZFVzZSlbLDFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhDbGF5dG9uQ291bnR5X2xhbmRVc2UpWywyXSwgY29sb3VyPUxhbmRfVXNlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE1LCBzaXplID0gNikgKw0KICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKEF0bGFudGFIaWdod2F5cywgZmlsdGVyKG1ldHJvX2NvdW50aWVzLCBOQU1FPT0iQ2xheXRvbiIpKSwgbGluZXdpZHRoPTEsIGNvbG9yID0gZGFya0dyYXkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IlBvcHVsYXRpb25cbkNoYW5nZSIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoQ2xheXRvbkNvdW50eSwgInBvcF9DaGFuZ2UiKSwgMSwgNSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKGdyYXksIiNGMEY5MjFGRiIpKSArIA0KICBnZW9tX3NmKGRhdGEgPSBmaWx0ZXIobWV0cm9fY291bnRpZXMsIE5BTUU9PSJDbGF5dG9uIiksIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJQcm9qZWN0ZWQgUG9wdWxhdGlvbiwgMjAyOTogQ2xheXRvbiBDb3VudHkiLA0KICAgICAgc3VidGl0bGUgPSAiV2l0aCBEZXZlbG9wZWQgYW5kIFVuc3VpdGFibGUgQXJlYXMgZnJvbSAyMDE5IikgKyANCiAgbWFwVGhlbWUgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLCBjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSksIG5jb2w9MikNCmBgYA0KDQpJbiBjb250cmFzdCB0byBIYWxsIENvdW50eSdzIHJlbGF0aXZlbHkgbGFyZ2UgYW1vdW50IG9mIGxhbmQgdGhhdCBpcyBzdWl0YWJsZSBmb3IgZGV2ZWxvcG1lbnQsIHdlIHNlZSB0aGF0IENsYXl0b24gQ291bnR5IGhhcyBhbG1vc3Qgbm9uZS4gVGhlIGNvdW50eSBpcyBhbG1vc3QgZW50aXJlbHkgZGV2ZWxvcGVkIGFuZCB0aGUgdHdvIGFyZWFzIGluIHRoZSBzb3V0aGVybiBwb3J0aW9uIG9mIHRoZSBjb3VudHkgdGhhdCBhcmUgbm90IGRldmVsb3BlZCBhcmUgZG9taW5hdGVkIGJ5IHdldGxhbmQgYXJlYXMgdGhhdCBhcmUgbm90IHN1aXRhYmxlIGZvciBkZXZlbG9wbWVudC4NCg0KRnVydGhlcm1vcmUsIHRoZSBtYXAgc2hvd3MgdGhhdCB0aGUgcmVtYWluaW5nIGNlbGxzIGFyZSBhbGwgbmVhciB3ZXRsYW5kIGFyZWFzIG9yIG5vdCBleHBlY3RlZCB0byBzZWUgcG9wdWxhdGlvbiBncm93dGguIFRoZXJlZm9yZSwgQ2xheXRvbiBDb3VudHkgaXMgYW4gaWRlYWwgbG9jYXRpb24gZm9yIGNvbnNlcnZhdGlvbiByYXRoZXIgdGhhbiBkZXZlbG9wbWVudCBpbiB0aGUgQXRsYW50YSBtZXRybyBhcmVhLg0KDQojIENvbmNsdXNpb24NCg0KVGhhbmsgeW91IGZvciB3YWxraW5nIHRocm91Z2ggb3VyIG1vZGVsLiBXaGF0IHdlIHRha2UgYXdheSBmcm9tIHRoaXMgaXMuLi4uDQo=